【问题标题】:Implementing Accept for UDP为 UDP 实现 Accept
【发布时间】:2021-08-05 00:52:32
【问题描述】:

UDP 套接字有一个“连接”调用,但没有对服务器应用程序的“接受”调用。有一些套接字 API 可以从连接的 UDP 套接字(例如 recvmmsg/sendmmsg)中获得性能优势,并且是具有非常高数据包速率的单流的最佳性能系统调用(任何更高的东西都需要内核绕过,例如DPDK)。

无论如何,我找不到为 UDP 服务器实现接受的解决方案,所以我的想法是执行以下操作:

  1. 服务器套接字一直监听直到收到来自客户端的数据包
  2. 服务器在套接字上调用 connect,从而允许使用加速连接的 API 将流量发送到客户端(即连接的 sendmmsg 超过未连接的 sendmmsg
  3. 服务器侦听 #1 的克隆套接字

为了解决接受的事情,我不知道如何克隆#1。我的“服务器”库的用户正在传递一个套接字文件描述符,这意味着他们可以控制他们配置的选项(SO_RECVBUFF 等)——我看不到它。不幸的是,为了克隆它,我现在需要这种可见性。

无论如何,如果有另一种方法来解决接受的事情,或者克隆套接字,我很想知道!谢谢!

【问题讨论】:

  • 你在使用多播吗?否则,同一端口上的多个 UDP 套接字的行为可能无法预测。
  • socket(7) 手册页列出了可用的套接字选项,以及如何获取或设置它们。
  • 这气味like an XY problem。你想解决什么问题?不,不是关于克隆套接字的问题,而是您认为解决方案是克隆套接字的问题,所以这就是您要问的问题。也许如果真正的问题得到了解释,一个更简单的解决方案将成为可能。
  • @Gabe 您将 TCP 上下文中的“连接”语义与 UDP 中的“连接”混淆了。 TCP 在设计上是一个面向连接的协议,因此 TCP 中的“连接”实际上是建立一个连接,而实际上接受连接的原始端口保持不变。 UDP 是无连接 - 从来没有连接。当您发送 UDP 数据包时,它完全独立于所有其他数据包,并且仅针对 IP/端口组合。 TCP 是一个电话呼叫 - 您呼叫并建立连接。 UDP 是一封信——谁去那个地址的邮箱,谁就得到它。

标签: c++ c linux sockets udp


【解决方案1】:

没有克隆套接字的内置功能。您需要跟踪您在一个套接字上设置的任何选项,并将它们设置在新的套接字上。

但是,您有一个更大的问题。如果您在同一个端口上打开了多个 UDP 套接字并且有一个单播数据包进入,那么这些套接字中只有一个会接收它并且您无法准确预测它将是哪一个。因此,为 UDP 连接提供“侦听”套接字和多个“接受”套接字的整个概念是行不通的。

您的程序将需要一个单独的套接字来处理所有传入的数据包并根据发送者对它们进行多路复用,可能有一个线程接收,每个客户端一个线程,但首先尝试不使用线程。

编辑:

鉴于使用了已连接的 UDP 套接字,您似乎可以将一个未连接的套接字作为侦听器,并为要与之通信的每个远程端点使用一个已连接的套接字。由于没有克隆功能,这意味着您为处理不同端点而创建的任何新套接字将在创建套接字和在该套接字上调用 connect 之间短暂处于未连接状态。在此期间,与连接的套接字无关的传入数据包可能会到达“侦听器”,或者它可能会在连接之前到达这个新套接字。您需要在您的应用程序中处理这种情况,很可能是通过让连接的套接字丢弃未知数据包并让客户端重试其初始“连接”直到收到响应。

【讨论】:

  • 在连接过程中,有一个竞态条件,但是在socket连接之后它是100%确定的。一旦调用 connect ,连接的套接字将只接收来自远程客户端的数据包。另一个未连接的套接字将接收所有其他流量。这是可以理解的。鉴于在初始连接期间还有其他方式丢失数据包(请参阅 unres_qlen),这是可以理解和接受的。
  • @Gabe 我认为这不是真的。您要连接的不是套接字,而是端点。如果多个套接字引用同一个端点,它们要么全部连接,要么都不连接。一些文档会说您正在连接“套接字”,但他们只是对条款草率。 (这对于 TCP 来说显然是错误的,对吗?)connect 所做的是在套接字引用的端点上执行“连接”操作,同时将任何其他引用连接到同一端点。
  • @DavidSchwartz 这是真的...引用连接文档If the socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received。我还测试并验证了文档。
  • @Gabe 我不明白你为什么认为这与我所说的相矛盾并支持你所说的。这并不意味着引用同一端点的另一个套接字也不会被连接。套接字的属性非常,基本上是围绕通信端点的薄包装。大多数属性设置函数不会设置套接字本身的属性,而是设置它引用的端点的属性,并且还会影响引用同一端点的其他套接字。端点不能被克隆或复制。
  • @DavidSchwartz 创建一个SOCK_DGRAM 套接字,设置SO_REUSEPORT,并绑定到IP 0.0.0.0 端口x。然后你创建另一个类似的套接字,然后在该套接字上使用connect 到给定的IP/端口(例如IP1:p1)。后一个套接字将从连接的 IP/端口获取数据包,前一个套接字将获取所有其他套接字。如果您随后创建连接到 IP2:p2 的第三个套接字,则该套接字将从 IP2:p2 获取数据包,而未连接的套接字将从除 IP1:p1 和 IP2:p2 之外的所有数据包获取数据包。
【解决方案2】:

你写的,

真正的问题是 udp 提供了“连接”但没有“接受”

,但不是,真正的问题是 UDP 不是面向连接的协议。正如the POSIX specifications for connect() 解释:

如果发起套接字不是连接模式,那么connect() 应该 设置套接字的对等地址,并且不建立连接。为了 SOCK_DGRAM 套接字,对等地址标识所有数据报的位置 在随后的send() 函数上发送,并限制远程发送者 用于后续的recv() 功能。 [...] 请注意,尽管 没有建立连接,术语“连接”用于描述一个 已设置对等地址的无连接模式套接字。

同样,UDP 不是连接模式协议。特别是,它是一种数据报协议。如果你想要一对套接字,每个套接字都将另一个设置为它们的对等点,那么你必须在两边都使用connect()。没有用于数据报的服务器套接字的概念,就连接套接字的工厂而言,即使有对等组,UDP 套接字仍然可以与其他端点通信。

如果你想用 UDP 模拟服务器套接字,那么你需要从一个监听已知端口的套接字开始。客户端将向该端口发送消息,并期望服务器在伪连接结束时在不同端口上建立一个 new、单独的套接字。服务器会从该套接字响应,告诉客户端它正在侦听哪个端口。

您可能希望使用这些初始消息的内容来确认每一方的意图,并确保服务器的初始响应与预期的“连接请求”正确配对。例如,可能客户端的初始消息是“CONNECT ”,而服务器的初始响应是“ACCEPTED ”。每个中的关键字确认消息的意图,匹配的 UUID(或其他键)允许客户端将服务器响应与正确的连接请求匹配。

但是,您还必须意识到 UDP 是一种不可靠的协议,并且您必须准备好适应这种情况。 UDP 数据报可能会被丢弃或丢失,并且它们的接收顺序可能与发送顺序不同。这是它如何实现比 TCP 更好的吞吐量的部分原因。如果您需要克服这些特征,那么您可以通过在 UDP 之上实现自己的更高级别协议来做到这一点,但在这一点上,您可能比刚开始使用 TCP 时更糟糕地点。

【讨论】:

  • 我不是在谈论协议。我说的是API。恐怕大多数在这里回答的人都会混淆这两者。就套接字 API 而言,“连接”意味着当使用任何“发送”变体时,不需要提供远程端点。当每秒发送一百万个数据包时,这个小优化确实加起来了。因此希望使用“连接的”套接字 API。请不要将协议与 API 混淆。
  • @Gabe,没有系统接口可以在对 all send 变体或 all @987654328 的调用中省略远程端点@ 变体,仅适用于 send()recv()。无论如何,我在这个答案中的大部分散文都致力于描述一种实现用户空间 API 的方法,我认为它与你想要的尽可能接近。了解系统协议实现的一些细节对于了解此类 API 可以或应该如何工作非常重要。
  • 约翰 - 这是不正确的。 send 实际上需要一个连接的套接字(write 也是如此),因为无法将远程端点作为接口的一部分提供。 Sendto, sendmsg, sendmmsg all 允许您指定远程端点,但如果套接字已连接,则应省略远程端点(例如,dest_addr 设置为 null)。文档引用:For a connected socket, these fields should be specified as NULL and 0, respectively。所以......所有发送变体都从连接的套接字中受益......在某些情况下,实际上需要连接的套接字(发送/写入)。
  • 怎么了,@Gabe?是的,send() 首先不允许您指定端点。因此它不能与未连接的套接字一起使用,但可以与已连接的套接字一起使用。是的,当其他与连接的套接字一起使用时,不应指定远程端点。这些都与我所说的不一致。
  • John - “没有提供从调用中省略远程端点的系统接口......仅针对 send() 和 recv()”。 -- 那是不正确的。
猜你喜欢
  • 1970-01-01
  • 2012-02-26
  • 2011-07-21
  • 1970-01-01
  • 2010-12-03
  • 2014-07-13
  • 1970-01-01
  • 2011-12-18
相关资源
最近更新 更多