LoginSignup
7
6

More than 5 years have passed since last update.

ElixirのSocketモジュールを使ってBroadcastを実装してみた

Last updated at Posted at 2018-03-11

ElixirのSocketモジュールを使ってWebRTCのシグナリングサーバーを
実装してみようかなと思ったのですが、
そもそもBroadcast部分も自分で実装しないといけない雰囲気だったので実装してみました

使用モジュールはelixir-socket のみです

lib/elixir_socket_testing/server.ex
defmodule ElixirSocketTesting.Server do

  alias ElixirSocketTesting.ClientAgent

  def start(port \\ 9000) do
    # クライアント保持用のAgent
    ClientAgent.start_link

    # WebSocketサーバーリッスン
    server = Socket.Web.listen! port

    loop(server)
  end

  def loop(server) do

    # クライアントからの接続を待ち受ける
    client = server |> Socket.Web.accept!
    client |> Socket.Web.accept!

    # クライアントをAgentに追加
    ClientAgent.add(client)

    # 各クライアントからのメッセージハンドリングは別プロセスで行う
    Task.async(fn -> recv(client) end)

    loop(server)
  end 

  def recv(client) do
    # クライアントからのメッセージをハンドリング
    case client |> Socket.Web.recv! do
      {:text, message} -> 
        broadcast({:text, message})
        recv(client)
      {:close, _, _} -> ClientAgent.remove(client)
      other ->  IO.inspect other
    end
  end

  def broadcast(packet) do
    # Agentに保持している全てのクライアントにメッセージを送信
    ClientAgent.all
    |> Enum.each(fn client -> Socket.Web.send(client, packet) end)
  end

end

以下の3点をやってるだけですね
- クライントのリストを保持しておくAgentを用意しておきクライアントから接続があったら
Agentにつっこんでおきます。
- クライアントからメッセージが来たら保持しているクライアント全てにメッセージを送信する
- クライアントからの接続が閉じたらリストから削除する

クライアントの保持用のAgentは以下のような感じになります

lib/elixir_socket_testing/client_agent.ex
defmodule ElixirSocketTesting.ClientAgent do

  def start_link do
    Agent.start_link(fn -> [] end, name: __MODULE__)
  end

  def add(client) do
    Agent.update(__MODULE__, fn list -> [client | list]  end)
  end

  def remove(client) do
    Agent.update(__MODULE__, fn list -> List.delete(list, client) end)
  end

  def all do
    Agent.get(__MODULE__, fn list -> list end)
  end
end

iexで動かしてみます

サーバー
$ iex -S mix
iex> ElixirSocketTesting.Server.start

別窓を3つほど立ち上げてそれぞれ接続して、
クライアント1, 2 でrecvしたあとにクライアント3でメッセージを送ってみました。

demo.gif

自分にもメッセージを送ってしまっていますが、他のクライアントにもメッセージが
送られているのがわかります。

簡単な実装ではありましたが思いがけずAgentとTaskの練習になった気がしています
Taskで非同期にしている部分などはSupervisorにぶら下げるとよりElixirでの
アプリケーションっぽくなるのかなぁ。。。と思ったり。

今回のコードはgithubにもあげていいます
elixir_socket_testing

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6