【问题标题】:Erlang udp workers - want to have more udp listeners on same portErlang udp 工作者 - 希望在同一个端口上有更多的 udp 侦听器
【发布时间】:2014-07-07 12:53:53
【问题描述】:

我正在用 Erlang 制作一个游戏服务器,我希望能够为每个连接的客户端拥有一个进程。这个过程应该负责处理客户端发送到服务器的所有 UDP 消息,通过处理它们并在必要时回复。

我如何实现这一目标?我知道使用 TCP 我可以放置任意数量的侦听器来运行该函数

gen_tcp:accept(ListenSocket)

它会阻止工作人员,直到工作可用等。我也希望这种行为与 udp 一样,但我一直在尝试制作一个简单的示例,该示例会生成一个新进程,该进程应该与每个 udp 数据包匹配,具体取决于它的 ip 和 port,看看是哪个玩家发送的,自己拿走,其余的忽略。

loop({Sock, Ip, Port}) ->
    receive
        {udp, Sock, Ip, Port, Msg} ->
            do_stuff & loop, etc.
    end
end.

这是否与循环中的 Ip 与消息相匹配?为此,我需要任何其他 UDP 侦听器与该特定数据包不匹配,以便工作人员在任何给定时间获取它应该获取的每条消息,对吧?如果另一个“通用”UDP 侦听器侦听,它可能会在工作人员之前选择消息。

所以我想问这种方法是否可行,或者我是否会收到错误消息说它不是套接字的所有者或类似的东西。到目前为止,我还没有让任何工作人员真正收到任何消息,所以我不确定是因为不匹配还是因为套接字只向当前控制进程发送消息,我想这会使我的计划无法实现。

【问题讨论】:

    标签: erlang udp pattern-matching actor


    【解决方案1】:

    您的问题标题和措辞表明您正在尝试使用 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.
    

    【讨论】:

    • 我知道两者的区别。我想使用 UDP,就好像它是 TCP 一样,我希望能够让一个特定的进程处理一个特定的客户端,因为我将通过 ip 而不是 nick 匹配游戏的玩家(所以我不必须解析字符串)。我知道它是无连接的,但这将由我通过超时等来实现。一旦玩家登录,就会产生一个进程,该进程将监听 UDP 套接字,但只会读取与其自己的客户端相关的消息。与IP匹配即是。这样做的原因是因为我希望能够拥有很多玩家。
    • 基本上,你没有回答我的问题。我的问题是关于模式匹配。匹配会起作用吗?还是只允许一个进程通过所述套接字接收消息,这意味着我不能将其传递给工作人员?
    • 抱歉不够清楚。您的服务器将只有一个 UDP 套接字,并且您建议的实现是不可能的。您关于模式匹配的问题实际上是无关的,但是是的,模式本身可以过滤传入的数据包。您可以匹配绑定变量(此处为SockIpPort)。但是,这是一个坏主意,因为所有未过滤的数据包都将保留并累积在消息队列中。
    • 但这会以其他方式处理。我只是对这种方法在技术上是否可行感兴趣。在同一个套接字上过滤来自不同进程的消息。所以这是相关的,因为我的问题实际上是我是否可以使用不同的进程来过滤同一个套接字。无论如何,这是我的问题,所以如果不清楚,我很抱歉。所有其他未过滤的消息将由主进程处理。
    • 不可能有多个进程从单个 UDP 套接字接收消息。
    猜你喜欢
    • 1970-01-01
    • 2011-02-18
    • 2013-10-13
    • 2012-04-19
    • 1970-01-01
    • 2017-06-04
    • 2017-09-23
    • 1970-01-01
    • 2013-11-05
    相关资源
    最近更新 更多