您的问题标题和措辞表明您正在尝试使用 TCP 等 UDP 套接字。
TCP 和 UDP 是不同的协议
TCP 是一种面向连接的协议。客户端连接到服务器,给定的服务器每个连接将有一个套接字,外加一个用于侦听。操作系统的网络堆栈将传入的数据包路由到侦听套接字或正确连接的套接字。
UDP 是无连接的。 您的程序将只有一个 UDP 套接字。它将接收到给定端口的所有 UDP 数据包。您不能生成一个进程并让它在同一个端口上侦听。所以你不能在同一个端口上有更多的 udp 侦听器,就像你在标题中写的那样。
考虑 Erlang 实现
TCP 的常见设计模式是让一个进程处理侦听套接字,为每个连接的套接字生成新的工作进程。
由于您尝试使用 UDP 实现类似的功能,因此您需要使用 TCP 执行网络堆栈为您执行的操作。您可以让一个进程接收数据包并将它们转发到适当的工作进程,如果需要,根据源端口和 IP 地址生成。此过程应维护所有活动“连接”的表,即所有活动的数据包“流”。
还请注意,UDP 没有关闭机制,因此您应该依赖超时,并且可能在您的协议中使用额外的机制。
不可能像您在 cmets 中建议的那样在每个进程上过滤单个套接字,因为处于活动模式的 Erlang 套接字将数据包发送到单个进程(控制进程)。你可以想象一些架构,其中数据包被广播到所有进程,每个进程都进行过滤以选择感兴趣的数据包。
但是,您会发现这在您的特定场景中没有意义,因为您还需要一种机制来确定是否应该生成工作进程,而这种机制最终会告诉您哪个进程应该处理传入的数据包。如果多个工作人员处理相同的消息,这可能是有意义的。
让我们用一些代码来具体看看:
server_loop(Workers) ->
receive
{udp, Sock, Ip, Port, Msg} = UDPPacket ->
% find out if we need to spawn a new worker.
% typically, Workers is a gb_trees:tree().
NewWorkers = case gb_trees:lookup({Ip, Port}, Workers) of
none ->
NewWorkerPid = spawn_link(fun() -> worker_loop(Ip, Port) end),
gb_trees:insert(NewWorkerPid, Workers);
{value, _WorkerPid} -> Workers %% <- look, we have the worker!
end,
% broadcast to all workers.
lists:foreach(fun({_, Worker} ->
Worker ! UDPPacket
end, gb_trees:to_list(NewWorkers)),
server_loop(NewWorkers);
% ... timeout callbacks from workers would go here
end.
这只是上面暗示的过于复杂的版本,其中数据包每次都被发送到适当的工作人员。
server_loop(Workers) ->
receive
{udp, Sock, Ip, Port, Msg} = UDPPacket ->
% find out if we need to spawn a new worker.
% typically, Workers is a gb_trees:tree().
{NewWorkers, Worker} = case gb_trees:lookup({Ip, Port}, Workers) of
none ->
NewWorkerPid = spawn_link(fun() -> worker_loop(Ip, Port) end),
{gb_trees:insert(NewWorkerPid, Workers), NewWorkerPid}
{value, WorkerPid} -> {Workers, WorkerPid}
end,
% send to worker.
Worker ! UDPPacket,
server_loop(NewWorkers);
% ... timeout callbacks from workers would go here
end.