我有一个相当令人困惑的问题。
我在Windows XP/7中使用一个大的C++库来处理UDP上的一些专有协议。它在整个程序运行期间监听一个端口,并等待来自远程对等方的连接。
大多数情况下,这很有效。但是,由于遇到了一些问题,我决定在调用WSARecvFrom之后直接添加一个简单的调试打印(库中使用的win32函数从感兴趣的套接字接收数据报,并告诉它们来自哪个ip和端口)。
奇怪的是,在某些情况下,我发现包在操作系统级被丢弃(即,我在wireshark中看到它们,它们有正确的dst端口,所有的校验和都是正确的——但它们从未出现在我植入代码的调试打印中)。
现在,我完全明白了“udp不能保证传输”这一事实(人们往往会太频繁地提及),但这与机器接收数据包无关,我在wireshark中看到了它们。
另外,我对操作系统缓冲区和填充的可能性很熟悉,但奇怪的是…
我做了一些研究,试图找出哪些包是掉下来的。我发现,所有丢弃的数据包都有两个共同点(尽管一些,但绝对不是大多数,没有丢弃的数据包也有这些共同点):
它们很小。协议中的许多数据包都很大,接近MTU,但是所有丢弃的数据包都小于100字节(总量)。
它们始终是两种类型中的一种:syn等价物(即对等方发送给我们的第一个数据包,以便发起通信)或fin等价物(即对等方不再有兴趣与我们交谈时发送的数据包)。
这两个特性中的任何一个会影响操作系统缓冲区,并导致数据包被随机丢弃(或者更有趣的是选择性地丢弃)?
对这一奇怪问题的任何澄清都将不胜感激。
非常感谢。
编辑(12年10月24日):
我想我可能漏掉了一个重要的细节。似乎在到达之前丢弃的数据包有一些共同点:它们(我开始相信,只有它们)是由“新”的对等方(即它以前从未尝试联系的对等方)发送到服务器的。
例如,如果syn等价的数据包来自一个我们以前从未见过的对等方*,那么它将不会被WSARecvFrom看到。但是,如果我们自己发送了一个与syn等价的包给该对等方(即使它当时没有回复),而现在它向我们发送了一个与syn等价的包,我们将看到它。
(*)我不确定这是一个我们没见过的对等端口(即ip:port)还是一个我们以前没见过的端口。
这有用吗?
这是我从没听说过的Winsock选项吗?(如上所述,代码不是我的,因此可能使用了我不知道的套接字选项)
再次感谢!

最佳答案

操作系统有一个固定大小的缓冲区,用于存储到达套接字但尚未被您读取的数据。当这个缓冲区耗尽时,它将开始丢弃数据。调试日志记录可能会延迟从套接字中提取数据的速率,从而增加溢出的可能性,从而加剧这种情况。
如果这是问题所在,您至少可以通过请求更大的recv缓冲区来减少它的实例。
您可以使用

int recvBufSize;
int err = getsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (char*)&recvBufSize, sizeof(recvBufSize));

你可以用
int recvBufSize = /* usage specific size */;
int err = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (const char*)&recvBufSize, sizeof(recvBufSize));

如果您仍然看到操作系统正在接收数据,但没有将其传递到socket客户机,那么您可以考虑不同的日志记录方法。例如
登录到一个ram缓冲区,并且只偶尔打印它(以您配置的最有效的大小)
来自低优先级线程的日志,要么接受这方面的内存需求是不可预测的,要么添加代码以在日志的缓冲区满时丢弃数据

07-25 23:51
查看更多