【问题标题】:UDP Packets, Seen by Wireshark, Dropped by (do not even reach) WSARecvFromUDP 数据包,被 Wireshark 看到,被丢弃(甚至没有到达)WSARecvFrom
【发布时间】:2012-10-01 19:39:23
【问题描述】:

我有一个相当令人困惑的问题。
我正在使用一个大型 C++ 库在 Windows XP/7 上通过 UDP 处理一些专有协议。它在程序运行的整个过程中侦听一个端口,并等待来自远方的连接。

大多数时候,这很有效。但是,由于我遇到的一些问题,我决定在调用 WSARecvFrom 之后直接添加一个简单的调试打印(库中用于从我感兴趣的套接字接收数据报的 win32 函数,并告诉什么 IP和他们来自的港口)。
奇怪的是,在某些情况下,我发现数据包在 OS 级别 被丢弃(即我在 Wireshark 中看到它们,它们具有正确的 dst 端口,所有校验和都是正确的 - 但它们从未出现在我植入代码的调试打印中)。

现在,我完全相信“UDP 不能保证传送”这一事实(人们往往提到的有点过于频繁)——但这并不相关,因为数据包已收到通过机器 - 我在 Wireshark 中看到它们。
另外,我熟悉操作系统缓冲区和被填满的可能性,但奇怪的部分来了……

我进行了一些研究,试图找出哪些数据包被准确丢弃。我发现,所有丢弃的数据包都有两个共同点(虽然一些,但绝对不是大多数不是 也放弃分享这些):

  1. 它们很小。协议中的许多数据包都很大,接近 MTU - 但所有丢弃的数据包都小于 100 字节(总)。
  2. 它们总是以下两种之一:一个 SYN 等效项(即对等方发送给我们以启动通信的第一个数据包)或 FIN 等效项(即对等方不再对通话感兴趣时发送的数据包)给我们)。

这两种品质中的任何一种都会影响操作系统缓冲区,并导致数据包被随机(甚至更有趣 - 有选择地)丢弃吗?
任何关于这个奇怪问题的线索将不胜感激。

非常感谢。


编辑(24/10/12):

我想我可能错过了一个重要的细节。似乎在到达之前丢弃的数据包有其他共同点:它们(我开始相信,只有它们)由“新”对等方发送到服务器,即它 之前没有尝试过联系。

例如,如果一个同步等效数据包从我们以前从未见过的对等点*到达,WSARecvFrom 将看不到它。然而,如果我们向我们自己的那个对等方发送了一个syn等效数据包(即使它当时没有回复),现在它向我们发送一个syn-等价的,我们会看到的。

(*) 我不确定这是一个我们没见过的peer(即ip:port)还是只是一个我们没见过的port之前。

这有帮助吗?
这是我从未听说过的某种 WinSock 选项吗? (如上所述,代码不是我的,所以它可能使用了我不知道的套接字选项)

再次感谢!

【问题讨论】:

  • 我认为它没有因为RecvFrom() from 参数或套接字绑定而被删除?
  • @Deanna:我坚信这是真的 - AFAIK from 问题应该使用 WSAGetLastError 引发一些错误代码(我得到的唯一一个是 WSAEWOULDBLOCK,AFAIK 意味着缓冲区中没有任何内容)片刻);套接字绑定应该没问题 - 接收和处理丢弃的一个(到同一端口)之前和之后的数据包。
  • 同一个IP?您是否从问题 IP 中得到任何信息?
  • @Deanna:是的,有时它是同一个 IP。例如,有一次我在大约 5 秒的时间内看到来自同一 IP 的 5 个几乎相同的同步等效数据包,都没有到达WSARecvFrom,但半秒后,我的代码启动了与该 IP 的连接(是的,它也可以启动)成功,并且它对我的seq-equivalent的回复被成功接收和处理。

标签: windows winapi sockets operating-system


【解决方案1】:

操作系统有一个固定大小的缓冲区,用于存储已到达您的套接字但您尚未读取的数据。当这个缓冲区用完时,它将开始丢弃数据。调试日志可能会通过延迟从套接字中提取数据的速率来加剧这种情况,从而增加溢出的机会。

如果这是问题所在,您至少可以通过请求更大的 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));

如果您仍然看到操作系统正在接收数据但没有传送到您的套接字客户端,您可以考虑使用不同的日志记录方法。例如

  • 记录到 RAM 缓冲区并仅偶尔打印(以您认为最有效的任何大小)
  • 从低优先级线程记录日志,要么接受对此的内存需求将是不可预测的,要么添加代码以在日志缓冲区已满时丢弃数据

【讨论】:

  • 感谢您的回答,但它仍然没有阐明被丢弃的数据包的具体特征(短的、syn-like 或 fin-like,仅数据包)。另外,我可能不够清楚,但日志记录并没有导致问题,我将其添加到调查问题。否则我不需要它:)
  • 我认为当套接字的 recv 缓冲区填满时,哪些数据会被丢弃,我认为没有任何定义的行为。您是否尝试过增加缓冲区大小以查看是否会减少丢弃数据包的数量?
  • 我会尝试查询和调整缓冲区大小,但我同意 - "I don't think there is any defined behaviour for what data gets dropped" - 因为我看到了一个非常独特的模式,我想知道除了缓冲区之外是否还有其他原因会导致这个问题?
  • 只有数据包被传送到应用程序。这是由 tcp/ip 和套接字设计的。 “大小为 100 或更小的数据包”让我认为您只有没有数据的标头。您能否确认这些数据包是否发往相同的 ip 和端口并且是否也包含数据?如果您可以发布一个小 pcap,其中包含一个已收到的数据包和另一个未收到的数据包,那就更好了
  • @fayyazkl - 感谢您的贡献。我提到的所有数据包都包含至少 30 个字节的 UDP 数据。 This pcap 包含两个尚未被 WSARecvFrom 接收的数据包,以及两个已经收到的数据包。 IP 是真实的,但我必须覆盖 UDP 数据(以及校验和)。
【解决方案2】:

我遇到了一个非常相似的问题,在确认接收缓冲区没有导致丢包后,我了解到这是因为我将接收超时设置得太低(1 毫秒)。将套接字设置为非阻塞并且不设置接收超时为我解决了这个问题。

【讨论】:

  • 你能分享一下需要准确更改的属性吗?
  • 我没有在套接字上设置“SO_RCVTIMEO”,而是将 FIONBIO 设置为 1 以启用非阻塞模式
【解决方案3】:

关闭 Windows 防火墙。

这样可以解决吗?如果是这样,您可以重新启用防火墙并为您的程序添加一条规则。

根据你在更新中所说的话,这是我最合乎逻辑的猜测:

似乎在到达之前丢弃的数据包共享其他东西 共同点:他们(我开始相信,只有他们)被送到 服务器由“新”对等方组成,即它没有尝试联系的对等方 之前。

【讨论】:

  • 如果 Windows 防火墙是问题所在,没有数据可以通过,即使是 Wireshark。
  • @EJP 这不是真的,至少对于 Vista 和 7 上的 TCP:wireshark 确实向我显示了防火墙阻止的传入 SYN。 (当我将防火墙更改为允许时,会显示 已发送相同的连接尝试。)我无法轻松测试 UDP,但如果它不同,我会感到惊讶。请记住,wireshark/winpcap 在某处插入网络堆栈,它查看进程级活动,例如 ProcMon。也就是说,我认为 Windows 防火墙不能解决这个问题。
  • @dave_thompson_085 具有相同经验的线鲨。我的防火墙允许一侧而不是另一侧。一旦我允许双方,应用程序就会收到所有数据包。
【解决方案4】:

在 redhat-linux 上也遇到了同样的问题。 这原来是一个路由问题。

RCA如下:

  1. 确实 UDP 能够到达目标机器(在 Wireshark 上看到)。
  2. 现在找不到源路由,因此在 Wireshark 上看不到回复。
  3. 在某些操作系统上,您可以在 Wireshark 上看到请求数据包,但操作系统实际上并未传递数据包套接字(您可以在 netstat-nap 中看到此套接字)。
  4. 在这种情况下,请始终检查 ping (ping <dest ip> -I<source ip>)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-12-19
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    相关资源
    最近更新 更多