c#,socket无法接收分片的UDP数据包

Posted

技术标签:

【中文标题】c#,socket无法接收分片的UDP数据包【英文标题】:c#, socket cannot receive fragmented UDP packets 【发布时间】:2018-04-20 16:30:17 【问题描述】:

我有一个嵌入式设备,它直接通过以太网连接到我的 PC。 设备通过 UDP 将音频流式传输到 pc,它发送 4096 字节的 UDP 数据包。

鉴于,以太网的MTU 是 1500 字节,数据包将被分段。

在 PC 上,我有一个 C# 程序,它尝试接收数据包并对其进行解码。 UDP 接收器在有效载荷小于 1500 字节时可以很好地接收数据包,但无法接收碎片数据包。

我通过Wireshark监控了传入的数据包,我可以看到数据包没有任何失败也没有丢弃。

我不知道问题出在我的代码中,还是 C# 套接字无法接收此类数据包。在这两种情况下,解决方案是什么?

第一次尝试:(使用 Socket)

Socket sokcet = new Socket(SocketType.Dgram, ProtocolType.Udp);
IPEndPoint ep = new IPEndPoint(IPAddress.Any,5222);
sokcet.Bind(ep);
int counter = 0;
while (true)

     if(sokcet.Available > 0)
          byte[] bytes = new byte[sokcet.Available];
          int receivedBytes = sokcet.Receive(bytes);

          string print = String.Format("Packet Received : 0,1", receivedBytes, counter);
          Console.WriteLine(print);
     

第二次尝试:(使用 UDPClient)

IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
UdpClient listener = new UdpClient(listenPort,AddressFamily.InterNetwork);
while (!done)
        
     byte[] bytes = listener.Receive(ref groupEP);

它们都不适用于大于 1500 字节的数据包。

更新 1: 我在 Loopback(127.0.0.1) 中测试了这个场景,我可以很好地接收到 4k UDP 消息。

更新 2: @Evk我从另一台通过交换机和路由器连接到我的电脑上测试了这个场景。现在我确信 C# 没有任何问题。不确定操作系统(win7 Ultimate 64x)。 我的嵌入式设备使用 LWIP,当用户使用 LWIP 发送大型 UDP 数据包时,有一些类似情况的报告。但我不确定这是我的情况。 我什至检查了 UDP 数据包的源地址和目标地址、校验和……但我无法弄清楚操作系统为什么会丢弃我的数据包。有没有什么工具可以分析网络包来判断是否有问题?

【问题讨论】:

发送数据时,接收方必须知道消息的结尾在哪里,并读取到结尾。所以使用以下之一 1) Ascii :用已知字符终止数据,不在消息中,如 '\n' 2) Ascii 或 Binary :将字节数添加到消息的开头 3) Ascii 或 Binary :使用固定长度的消息。 问题不在于长度变化,问题在于数据包负载超过 1500 字节时 'sokcet.Receive(bytes);'在第一个例子中和'listener.Receive(ref groupEP);'在第二个例子中不要执行。 “当发送数据时,接收者必须知道消息的结尾在哪里并且读到结尾”——@jdweng 的这句话在这里完全不相关,因为你正在使用UDP,一种为您构建消息的协议。您可以放心地忽略他们的评论。至于您的实际问题,当没有一个好的minimal reproducible example 可以可靠地重现问题时,很难提供任何有用的建议,更不用说一个好的答案了。假设您的代码运行良好,那么您的问题更有可能在于网络配置而不是代码本身。 @jdweng UDP 是面向消息的协议(与面向流的 TCP 不同)。 UDP中的“D”是可以被认为是消息的数据报。您所说的对于 TCP 是正确的,但对于 UDP 不是。当您通过 UDP 发送某些内容时 - 您将其作为单个“块”发送。当您通过 UDP 接收某些内容时 - 您再次将其作为单个块进行。你可以看到 UdpClient 有简单的Receive 方法,它返回字节数组而不需要你指定大小。 @jdweng UDP 片段的最大大小略小于 64K 字节。它可以在多个 IP 数据包上分段,但将在接收端由网络堆栈重新组装。如果数据报的任何片段丢失 - 整个数据报都会丢失。你永远不会在你的套接字中收到部分数据报。 【参考方案1】:

我会确定这些消息实际上是传送到您进程的 UDP 接收队列的。您可能会看到它是 Wireshark,但这并不能保证它以后不会在 UDP 协议级别被丢弃或过滤。

我会打开一个命令窗口 (CMD) 并在您发送流量并运行解码应用程序时循环运行它(另外,停止其他可能使用 UDP 的应用程序,因为它们可能会干扰这些数字):

> netstat -s -p udp

UDP Statistics for IPv4

  Datagrams Received    = 48775          <======= [This *should* increase]
  No Ports              = 83823          
  Receive Errors        = 16367          <====== [ WATCH THIS]
  Datagrams Sent        = 130194

如果您发现当您的应用程序在那里并且似乎没有处理时出现接收错误......那么有可能由于某种原因超过 1500 字节的数据包被 UDP 丢弃。您可能需要增加应用程序的套接字接收缓冲区或寻找可能导致 > MTU 数据包在接收时被丢弃的 Windows UDP 网络配置选项。

【讨论】:

感谢您的帮助,“接收到的数据报”几乎没有增加,“接收错误”增加得如此之快。我什至将 Windows 接收器缓冲区大小增加到 10240,默认为 8192,但没有任何变化

以上是关于c#,socket无法接收分片的UDP数据包的主要内容,如果未能解决你的问题,请参考以下文章

无法使用/ AsyncUdp Socket 在 Iphone 上接收 UDP 数据包

C#中UDP(Socket)

Unity 游戏内无法接收 UDP 数据包

C# UDP Socket:获取接收者地址

Java UDP 客户端不接收数据包

udp Socket 发送缓冲区 最大可以为多大