elixir - 如何编辑回显服务器以允许多个客户端交换消息
问题描述
我创建了一个没有客户端代码的服务器。我只需要在终端 telnet 127.0.0.1 4001 和另一个终端 telnet 127.0.0.2 4001 上输入,所以当我在第一个终端上输入一条消息时,它会出现在同一个终端上,我知道这是一个回显服务器,我想要的只是是在可能的情况下修改此代码,以便其他终端上的其他客户端可以接收该消息。这是一个回显服务器代码:
defmodule Multichat.Server do
require Logger
def accept(port) do
{:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true])
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
{:ok, pid} = Task.Supervisor.start_child(Multichat.Server.TaskSupervisor, fn -> serve(client) end)
:ok = :gen_tcp.controlling_process(client, pid)
loop_acceptor(socket)
end
defp serve(socket) do
socket
|> read_line()
|> write_line(socket)
serve(socket)
end
defp read_line(socket) do
{:ok, data} = :gen_tcp.recv(socket, 0)
data
end
defp write_line(line, socket) do
:gen_tcp.send(socket, line)
end
end
我应该改变什么,所以当我使用 telnet 打开许多客户端时,他们会收到来自彼此的消息
解决方案
我认为实现这一点的最简单方法是在active
模式下设置您的套接字并在GenServer
. 然后,通过维护所有客户端处理程序的列表,您可以遍历该列表并向每个客户端发送消息。一个有效但不是很干净的版本:
defmodule Multichat.ClientConnection do
use GenServer
def start_link(socket), do: GenServer.start_link(__MODULE__, socket)
def handle_call({:send, message}, _from, socket) do
:gen_tcp.send(socket, message)
{:reply, :ok, socket}
end
def handle_info({:tcp, _socket, message}, socket) do
for {_, pid, _, _} <- DynamicSupervisor.which_children(Multichat.Server.ConnectionSupervisor) do
if pid != self() do
GenServer.call(pid, {:send, message})
end
end
{:noreply, socket}
end
end
defmodule Multichat.Server do
# ...
def accept(port) do
{:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: true, reuseaddr: true])
Logger.info "Accepting connections on port #{port}"
loop_acceptor(socket)
end
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
{:ok, pid} = DynamicSupervisor.start_child(Multichat.Server.ConnectionSupervisor, {Multichat.ClientConnection, client})
:ok = :gen_tcp.controlling_process(client, pid)
loop_acceptor(socket)
end
# ...
end
在这里,我DynamicSupervisor.which_children/1
用来获取已启动的客户端处理程序的列表。Registry
为此目的使用 a 可能是一个更好的主意。
推荐阅读
- r - purrr:: map 和 lag 函数,嵌套数据框的语法
- javascript - 为什么我在谷歌地图上显示标记的脚本不起作用?
- mysql - MySql 将行放入单个 JSON 对象中
- python - 我想设置 cookie 字段和字符集来使用 curl 命令处理汉字
- r - 根据字符串条件将每小时数据折叠为每日数据并创建虚拟变量
- angular - Jasmine:监视由另一个函数调用的导出函数不起作用
- .net - IIS 服务器中的 SSL 证书 (.pfx) 文件问题
- git-config - 是否可以在 .gitconfig 中编写多行别名?
- c++ - 未选中复选框时,有没有办法隐藏按钮?
- sql - 如何在 SQL/Spark/GraphFrames 中进行这种转换