问题描述
我有一个相当令人困惑的问题.
我正在使用一个大型 C++ 库在 Windows XP/7 上通过 UDP 处理一些专有协议.它在程序运行的整个过程中侦听一个端口,并等待来自远方的连接.
I have quite a bewildering problem.
I'm using a big C++ library for handling some proprietary protocol over UDP on Windows XP/7. It listens on one port throughout the run of the program, and waits for connections from distant peers.
大多数时候,这很有效.但是,由于我遇到的一些问题,我决定在调用 WSARecvFrom
之后直接添加一个简单的调试打印(库中用于从我感兴趣的套接字中接收数据报的 win32 函数,并告诉他们来自哪个 IP 和端口).
奇怪的是,在某些情况下,我发现数据包在 OS 级别 被丢弃(即我在 Wireshark 中看到它们,它们具有正确的 dst 端口,所有校验和都是正确的 - 但它们从未出现在我植入代码的调试打印中).
Most of the time, this works well. However, due to some problems I'd experienced, I've decided to add a simple debug print directly after the call to WSARecvFrom
(the win32 function used in the library to recv datagrams from my socket of interest, and tell what IP and port they came from).
Strangely enough, in some cases, I've discovered packets are dropped at the OS level (i.e. I see them in Wireshark, they have the right dst-port, all checksums are correct - but they never appear in the debug prints I've implanted into the code).
现在,我完全相信UDP 不能保证传送"这一事实(人们往往提到的有点过于频繁)——但这并不相关,因为数据包已收到通过机器 - 我在 Wireshark 中看到它们.
另外,我熟悉操作系统缓冲区和被填满的可能性,但奇怪的部分来了……
Now, I'm fully of the fact (people tend to mention a bit too often) that "UDP doesn't guarantee delivery" - but this is not relevant, as the packets are received by the machine - I see them in Wireshark.
Also, I'm familiar with OS buffers and the potential to fill up, but here comes the weird part...
我进行了一些研究,试图找出哪些数据包被准确丢弃.我发现,所有丢弃的数据包都有两个共同点(虽然一些,但绝对不是大多数,不是 也放弃分享这些):
I've done some research trying to find out which packets are dropped exactly. What I've discovered, is that all dropped packets share two things in common (though some, but definitely not most, of the packets that aren't dropped share these as well):
- 它们很小.协议中的许多数据包都很大,接近 MTU - 但所有丢弃的数据包都小于 100 字节(总).
- 它们始终是以下两种之一:SYN 等效项(即对等方发送给我们以启动通信的第一个数据包)或 FIN 等效项(即对等方不再对通话感兴趣时发送的数据包)给我们).
这两种品质中的任何一种都会影响操作系统缓冲区,并导致数据包被随机(甚至更有趣 - 有选择地)丢弃吗?
任何关于这个奇怪问题的线索将不胜感激.
Can either one of these two qualities affect the OS buffers, and cause packets to be randomly (or even more interesting - selectively) dropped?
Any light shed on this strange issue would be very appreciated.
非常感谢.
编辑(24/10/12):
我想我可能错过了一个重要的细节.似乎在到达之前丢弃的数据包有其他共同点:它们(我开始相信,只有它们)由新"对等方发送到服务器,即它之前没有尝试过联系.
I think I may have missed an important detail. It seems that the packets dropped before arrival share something else in common: They (and I'm starting to believe, only they) are sent to the server by "new" peers, i.e. peers that it hasn't tried to contact before.
例如,如果一个同步等效数据包从我们以前从未见过的对等点*到达,则 WSARecvFrom
将不会看到它.然而,如果我们向我们自己的那个对等方发送了一个syn等效数据包(即使它当时没有回复),现在它向我们发送一个syn-等价的,我们会看到的.
For example, if a syn-equivalent packet arrives from a peer* we've never seen before, it will not be seen by WSARecvFrom
. However, if we have sent a syn-equivalent packet to that peer ourselves (even if it didn't reply at the time), and now it sends us a syn-equivalent, we will see it.
(*) 我不确定这是一个我们没见过的peer(即ip:port)还是只是一个我们没见过的port之前.
(*) I'm not sure whether this is a peer we haven't seen (i.e. ip:port) or just a port we haven't seen before.
这有帮助吗?
这是我从未听说过的某种 WinSock 选项吗?(如上所述,代码不是我的,所以它可能使用了我不知道的套接字选项)
Does this help?
Is this some kind of WinSock option I've never heard of? (as I stated above, the code is not mine, so it may be using socket options I'm not aware of)
再次感谢!
推荐答案
操作系统有一个固定大小的缓冲区,用于存储已到达您的套接字但尚未被您读取的数据.当这个缓冲区用完时,它将开始丢弃数据.调试日志可能会通过延迟从套接字中提取数据的速率来加剧这种情况,从而增加溢出的机会.
The OS has a fixed size buffer for data that has arrived at your socket but hasn't yet been read by you. When this buffer is exhausted, it'll start to discard data. Debug logging may exacerbate this by delaying the rate you pull data from the socket at, increasing the chances of overflows.
如果这是问题所在,您至少可以通过请求更大的 recv 缓冲区来减少它的实例.
If this is the problem, you could at least reduce the instances of it by requesting a larger recv buffer.
您可以使用检查套接字接收缓冲区的大小
You can check the size of your socket's recv buffer using
int recvBufSize;
int err = getsockopt(socket, SOL_SOCKET, SO_RCVBUF,
(char*)&recvBufSize, sizeof(recvBufSize));
您可以使用将其设置为更大的尺寸
and you can set it to a larger size using
int recvBufSize = /* usage specific size */;
int err = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
(const char*)&recvBufSize, sizeof(recvBufSize));
如果您仍然看到操作系统正在接收数据但没有传送到您的套接字客户端,您可以考虑使用不同的日志记录方法.例如
If you still see data being received by the OS but not delivered to your socket client, you could think about different approaches to logging. e.g.
- 记录到 RAM 缓冲区并仅偶尔打印(以您认为最有效的任何大小)
- 来自低优先级线程的日志,要么接受对此的内存需求将是不可预测的,要么添加代码以在日志缓冲区已满时丢弃数据
这篇关于UDP 数据包,被 Wireshark 看到,被丢弃(甚至没有到达)WSARecvFrom的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!