Edited at

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

More than 1 year has passed since last update.

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