【问题标题】:Multiple UDP sockets and multiple clients多个 UDP 套接字和多个客户端
【发布时间】:2018-02-04 20:23:33
【问题描述】:

我必须使用 UDP 套接字编写程序,但我无法理解在我遇到的情况下应该做什么。

我有一个服务器和一个客户端。服务器在 INADDR_ANY 套接字上接收数据报,并且对于出现的每个唯一客户端,它会向其发送大量信息。每个客户端还定期向服务器发送数据。

对于每个联系侦听套接字的客户端,我们将其称为 0,我为其创建一个单独的套接字,这样我就可以向它发送数据而不会阻塞套接字 0。我想要做的是将与特定客户端的所有通信移动到新的插座。我可以这样做吗?目标是让通信更轻松、更高效并避免阻塞任何套接字。

所以我未能找到答案的问题是:

  • 我可以从该特定客户端接收来自该套接字的数据吗?
  • 如果不是,如果我不经常执行 recvfrom,所有客户端都不会写入套接字 0 会阻塞它吗?
  • 如果可以在单独的套接字上接收来自特定客户端的数据,该数据是否会同时到达套接字 0 和特定套接字?

我知道 TCP 会更好地解决这个问题,但我必须使用 UDP。我该怎么做?是否有处理此类情况的公认“标准”?

评论:我感觉我误解了 UDP 套接字,但与 TCP 相比,关于它的教程真的很少。

【问题讨论】:

  • 确实没有“阻塞套接字 0”这样的东西。如果发送会被阻塞,那么你有多少个套接字都没有关系。 UDP 服务器最好使用单个套接字来实现。
  • @EJP 真的吗?我的意思是套接字的内存量是有限的。如果套接字已满,发送到它的 sendto() 会阻塞,如果非阻塞则失败,对吧?我觉得为每个客户端创建一个 UDP 套接字是合乎逻辑的。我的逻辑有什么问题?
  • 与 TCP 套接字不同,UDP 套接字总是以网络接口接受它的速度发送数据。因此,通常您不需要例如两个 UDP 套接字;您可以改为创建一个具有两倍 SO_SNDBUF 缓冲区空间的 UDP 套接字。
  • @m_highlanderish 套接字具有套接字发送和接收缓冲区,您可以控制其大小。

标签: sockets udp client-server


【解决方案1】:

我想要做的是将与特定客户的所有通信转移到 新的插座。我可以这样做吗?

您可以创建一个新的 UDP 套接字,将其绑定()到不同的端口,然后指示客户端开始将流量发送到新套接字的端口而不是通常的端口;但请注意,这样做并没有任何好处。

如果没有,如果我不让所有客户端写入套接字 0 会阻塞它 不会经常做 recvfrom 吗?

是的,但通常的解决方案是经常调用 recvfrom() 以跟上传入流量的流量,和/或增加其 SO_RECVBUF 缓冲区大小以使其传入数据缓冲区足够大以至于不太可能变得饱满。 确保经常调用 recvfrom() 的一种方法是创建一个单独的线程,该线程除了在循环中调用 recvfrom() 之外什么都不做,然后将数据交给另一个线程进行更密集的处理。如果可能,以更高的优先级运行此网络线程,以确保它不会被其他线程占用 CPU。

如果可以在单独的服务器上接收来自特定客户端的数据 套接字,该数据会同时到达套接字 0 和特定套接字吗?

如果您有两个线程都在同一个套接字上调用 recvfrom(),那么任何给定的传入 UDP 数据包都将被传递到一个线程或另一个线程,并且无法预测哪个线程将接收哪个数据包 - 它会取决于特定线程在其上调用 recvfrom() 时套接字传入缓冲区中的下一个数据包是什么,只是运气好。通常不推荐使用多个线程访问单个套接字的设计。

我知道 TCP 会更好地解决这个问题,但我必须使用 UDP。如何 我应该这样做吗?是否有一个公认的“标准”来处理此类 情况?

我不知道什么是“标准”,但我通常有一个专用的 I/O 线程,它除了从 UDP 套接字读取(并在必要时写入)之外什么都不做。它将 UDP 套接字设置为非阻塞模式,然后循环 select() 到 recvfrom() 任何传入的 UDP 数据包,并(以线程安全的方式)将传入数据包的数据及其源地址/端口信息附加到其他(时间敏感度较低的)线程的 FIFO 队列,以便稍后取出并处理。这样,即使一个数据包(或一系列或多个数据包)需要相对较长的时间来处理,结果也不会是数据包被丢弃(尽管随着 FIFO 变大它可能会暂时增加 RAM 使用量)

【讨论】:

  • 很好的答案,虽然我不是在问一个套接字上有两个线程,而是有两个套接字,如果我的客户端发送数据,哪个套接字将接收数据(一个监听所有端口和其他的,例如,连接()到这个客户地址)。我知道我应该怎么做,但我不知道为什么。为什么使用更多的套接字,例如,创建一个单独的套接字来发送给每个客户端(不是recv,只是发送)没有做任何事情?为什么总是使用一个插座更好?我的意思是,在套接字上使用 connect() 将使发送到它更容易。
  • 对第三段的问题。查看此链接:masterraghu.com/subjects/np/introduction/… 这表明我可以创建一个 UDP 套接字并将其连接到特定地址和端口。这正是我想要的。现在,如果我有一个绑定到 IPADDR_ANY 的套接字并创建一个新套接字,我将连接到从 IPADDR_ANY 获取的地址 recfrom() ,来自该地址的数据报最终会在新套接字中没有问题吗?有碰撞吗?我必须使用 SO_REUSEPORT 吗?如果我将一个绑定到 IPADDR_ANY,我什至可以创建一个新的套接字吗?
  • 根据我的经验,在 UDP 套接字上调用 connect() 会做两件事:(1) 它允许您在套接字上调用 send()(或 write()),并且数据包是发送到您 connect() 的地址,并且 (2) 它在套接字上安装一个过滤器,以便源地址和/或源端口与 connect() 调用中指定的不同的数据包不会被传递通过该套接字到您的代码。 (1) 有点方便(没有它就可以很容易地调用 sendto()),并且 (2) 对我来说是一个问题,因为在我的应用程序中我想处理所有传入的 UDP 数据包,而不仅仅是来自一个来源的数据包。
  • 关于您的第二条评论,我自己还没有尝试过这种技术(所以请随意尝试,看看它是否适合您),但您肯定需要同时执行 SO_REUSEADDR(如果也可能是 SO_REUSEPORT),以便让两个 UDP 套接字绑定同一个端口,即使这样,我的直觉是,这两个套接字中的哪一个接收任何特定数据包将是不确定的/不可预测的——如果你的 connect()' d socket 接收到别人的数据包,我怀疑数据包会被丢弃/过滤。
【解决方案2】:

@ Jeremy Friesner:您写道:“……即使如此,我的直觉是,两个套接字中的哪一个接收任何特定数据包将是不确定的/不可预测的——如果您的 connect() 的套接字接收别人的数据包,我怀疑数据包会被丢弃/过滤”。 正是这一点,我在 Linux 和 lwIP 中进行了调查,结果是:后来创建的套接字(具有更高的 fd 号)是接收的,而另一个没有接收。

【讨论】:

    猜你喜欢
    • 2016-05-09
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 2016-02-14
    • 1970-01-01
    • 2011-03-22
    相关资源
    最近更新 更多