为 UDP 模拟 accept()(设置解复用 UDP 套接字的时间问题)

Posted

技术标签:

【中文标题】为 UDP 模拟 accept()(设置解复用 UDP 套接字的时间问题)【英文标题】:Emulating accept() for UDP (timing-issue in setting up demultiplexed UDP sockets) 【发布时间】:2009-06-30 16:10:50 【问题描述】:

对于具有长期连接的 UDP 服务器架构,一种架构是拥有一个侦听所有传入 UDP 流量的套接字,然后使用 connect() 为每个连接创建单独的套接字来设置远程地址。我的问题是,是否有可能像 accept() 对 TCP 所做的那样以原子方式执行此操作。

创建单独的套接字并使用 connect() 的原因是,这样可以很容易地将数据包处理分散到多个线程中,并且也可以更容易地使套接字直接与所需的数据结构相关联加工。 网络堆栈中的多路分离逻辑将传入的数据包路由到最具体的套接字。

现在我的问题基本上是当人们想像这样模拟 UDP 的 accept() 时会发生什么:

    将 select() 与包含 UDP 服务器套接字的 fd 集一起使用。

    然后从 UDP server-socket 读取一个数据包。

    然后创建一个新的 UDP 套接字,然后将其 connect() 到远程地址

    我使用包含两个套接字的 fd-set 调用 select()。

    返回什么?

假设数据包在 1 到 3 之间到达操作系统。

数据包会被解复用到 UDP 服务器套接字,还是会被解复用到在 3 中创建的更具体的套接字。也就是说,解复用发生在什么时候?当数据包到达时,还是必须“好像”它到达第 4 点?

如果上述方法不起作用,后续问题:最好的方法是什么?

【问题讨论】:

你确定你甚至可以创建一个连接到另一端的新 UDP 套接字,并且仍然保持相同的服务器端端口吗?您通常做的是在服务器端的不同端口上创建一个 UDP 套接字,“服务器套接字”仅用于初始请求,与对等方的进一步通信发生在 2 个完全不同的端口上。或者只是在服务器上使用 1 个套接字,而不关心连接它,你只需要记下对等地址。 UPD 无论如何都是无连接的。 【参考方案1】:

我看到这个讨论是从 2009 年开始的,但由于我搜索时它不断弹出,我想我应该分享我的方法。既是为了获得一些反馈,也是因为我很好奇问题的作者是如何解决问题的。

我选择 emulate UDP-accept 的方式是 nik 的回答中第一和第二的组合。我有一个监听给定套接字的根线程。为简单起见,我选择使用 TCP,但是将此套接字更改为 UDP 并不难。当客户端想要使用 UDP “连接”到我的服务器时,它首先连接到 TCP 套接字并请求新的连接。

然后根线程继续创建 UDP 套接字,将其绑定到本地接口,进行连接并设置数据结构。然后将此文件描述符传递给负责连接的线程。新的 UDP 套接字的 IP/端口信息被传回给客户端,客户端创建一个新的 UDP 套接字并将数据发送到提供的 IP/端口。

这种方法非常适合我的使用,但设置流的额外步骤会带来开销。在某些情况下,这种开销可能是不可接受的。

【讨论】:

【参考方案2】:

我在这里问自己后发现了这个问题......

UDP server and connected sockets

由于 UDP 可以使用 connect() 来指定对等地址,我想知道为什么不能使用 accept() 从服务器端有效地完成连接的 UDP 会话。它甚至可以将触发 accept() 的数据报(以及来自同一客户端的任何其他数据报)移动到新的描述符。

这将实现更好的服务器可扩展性(有关更多背景信息,请参阅 SO_REUSEPORT 背后的原理)以及可靠的 DTLS 身份验证。

【讨论】:

【参考方案3】:

这行不通。 你有两个简单的选择。

    创建一个多线程程序,该程序有一个“根”线程侦听 UDP 套接字,并根据源将接收到的数据包“分派”到正确的线程。这是因为您希望按来源隔离处理。

    扩展您的协议,使源接受某个固定端口上的传入连接,然后继续协议通信。在这种情况下,您将让源请求在标准 UDP 端口(您选择)上,然后您的终端将从新的 UDP 套接字响应源的 UDP 端口。这样,您就启动了一条新的 UDP 路径,从您的一端向后到每个源的已知 UDP 端口。这样一来,您就有了不同的 UDP 套接字。

【讨论】:

为什么这不起作用?请参阅lxr.linux.no/linux-bk+v2.6.5/net/ipv4/udp.c#L222 以获取将解复用到最特定套接字的操作系统示例。我对您提出的解决方案的问题是解决方案 1 可能无法扩展,解决方案 2 生成的数据包可能无法到达某些 NAT 后面的客户端。 好吧,我不知道你愿意在套接字层下编码。

以上是关于为 UDP 模拟 accept()(设置解复用 UDP 套接字的时间问题)的主要内容,如果未能解决你的问题,请参考以下文章

浅谈I/O多路复用技术

IO多路复用互动聊天,select函数监听

Socket编程(在控制台模拟聊天功能)

I/O多路复用

mux复用 demux解复用

ffmpeg解复用编解码 常用API大全给出详细中文解释