C++、UDP、sendto 需要延迟才能工作

C++, UDP, sendto needs delay to work

我正在用 C++ 编写 UDP 中继服务器。但是我有一个问题。

我有一个基本循环,只调用 recvfrom(),检查数据包中的错误,然后从数据包中读取 "target" 并在同一个套接字上使用 sendto()向也在同一服务器上中继的目标客户端发送数据包。

问题是,如果我不在 sendto() 之前添加延迟,几乎所有数据包都会丢失(这个延迟取决于连接速度,所以我不能静态设置它)。

m_iSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bind(m_iSocket, (SOCKADDR*)&m_oSockAddress, sizeof(SOCKADDR_IN));
...
while(true) {
    recvfrom(m_iSocket, (char*)m_pRecvBuffer, m_nBufferSize, 0, (SOCKADDR*)&remoteAddr, &remoteAddrLen);
    ...
    sendto(m_iSocket, reinterpret_cast<char*>(rw.getData()), rw.getBufferSize(), 0, targets address , address size);
}

有什么想法吗?

Any ideas?

很可能您的传出 UDP 数据包被丢弃,因为某处缓冲区溢出。它最有可能溢出的地方是您的套接字自己的传出数据缓冲区;如果是这样,您可以通过调用 setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, ...) 使套接字的传出缓冲区大小更大(即大到足以发送保留您通过 sendto() 调用传递的所有数据包)来减少丢弃的数据包数量一次)。

如果这还不足以解决问题,那么您接下来可以做的就是实现您自己的应用程序内缓冲;也就是说,不是立即调用 sendto(),而是将数据包数据推送到 FIFO 数据结构的尾部,然后仅在套接字 select() 准备好写入时才调用 sendto()(并且当发生这种情况时,从 FIFO 的头部弹出下一个数据包并用它调用 sendto())。这样一来,您始终只以套接字缓冲区可以接受的速度发送数据,而不是假设套接字缓冲区总是足够大以立即接受您扔给它的所有内容。

(缓冲区可能溢出的另一个地方是接收应用程序的套接字的接收缓冲区,在这种情况下,在接收应用程序的套接字上调用 setsockopt(s, SOL_SOCKET, SO_RCVBUF, ...) 可能也会有所帮助)

目标进程的读取速度不够快,因此正在丢弃数据报,因为它的套接字接收缓冲区已满。那么问题来了,什么是正确的缓解措施:

  1. (如果可以的话)加快接收速度。
  2. 在循环中添加延迟。这只会将问题转移到向您发送这些数据报的任何人身上。当您的套接字接收器缓冲区已满时,将丢弃入站数据报。
  3. 什么都不做。这不是你的问题;你的代码是正确的。

如果可能,我推荐 (1),以及 (3)。不要在网络代码中添加延迟。他们不解决问题:他们只改变问题。

如果您可以访问所有相关源代码,则应增加所有相关套接字发送和接收缓冲区的大小。