日期:2014-05-18  浏览次数:21054 次

一个数据包传送的奇怪问题
用UdpClient在两个设备之间传送byte[],一个Send,一个Receive,99%都OK。
但是,在某些特定的情况下,会发生丢包。一边Send之后,另一边Receive没反应。这种特定情况感觉很随机,与数据和设备有关,因为同样的设备把传送的byte[]换几个字节,或者同样的byte[]放到其他设备上传送,就没问题。
用WireShark查看,正常情况下在发送方和接收方都是一条同样的UDP包信息。而在异常的情况下,发送方的UDP包的报头里面Flags变成了0x01(More Fragments),但Fragment offset还是0。Data也被截掉了一部分。紧接着会产生一个IPv4包 Fragmented IP rotocol(proto=UDP 0x11,off=1480,ID=4573),Flags为0,Fragment offset为1480。此时WireShark显示接收方收到一个UDP包,但是整个Frame的Length比发送出来的要短得多,Flags为0,Fragment offset为1480。但Data里面却有完整的数据。这样的UDP包不能被UdpClient捕获到。
由此看来包被分割了。即使指定了UdpClient.Client.SendBufferSize为一个远大于byte[]长度的值也没用,而如果指定UdpClient.DontFragment = true则会报“一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小”的错误。看来是受硬件设备的影响。但是奇怪的是似乎与byte[]的长度没关系,传送失败后在byte[]中再添加几个字节又能传送成功。这是为什么呢?系统分割数据包的方式是怎样的?当然我可以自己去分割,但是这样牵扯到太多了。所以只能希望有什么办法能够在现有的基础上作调整。
有谁遇到过这个问题没?精通网络编程的说说有什么想法,谢谢。这两天被这个问题折腾得够呛。

------解决方案--------------------
UDP不会自动拆包,必须在发送前自行将数据拆分后发送,超过1500的必须拆分,安全起见设定为1400。
另外为了UDP传输的安全性,每个数据包的前面第一个字节必须用来存放当前包序号,为了序号不连续的丢包现象能及时被发现并重传,而整个数据的大小应该在序号为0的数据包后仅跟着4字节长度(int类型)来指明。
------解决方案--------------------
如果你传超过以太网帧(1500字节左右)的数据,udp就不太适合,否则udp可能乱序,你自己维护序列号得烦死你


------解决方案--------------------
探讨

引用:
UDP不会自动拆包,必须在发送前自行将数据拆分后发送,超过1500的必须拆分,安全起见设定为1400。
另外为了UDP传输的安全性,每个数据包的前面第一个字节必须用来存放当前包序号,为了序号不连续的丢包现象能及时被发现并重传,而整个数据的大小应该在序号为0的数据包后仅跟着4字节长度(int类型)来指明。


可是为什么99%的情况下传几十k的数据都没问题呢?我……

------解决方案--------------------
探讨

引用:
如果你传超过以太网帧(1500字节左右)的数据,udp就不太适合,否则udp可能乱序,你自己维护序列号得烦死你


用于文件传输的,Manager的决定。早就跟他说过用UDP不好了,无奈。

------解决方案--------------------
能说说你们manager用udp的理由么,如果说效率,那是杞人忧天,假设百兆以太网,极限速率12.5MB/s,你用tcp,即便协议开销大些,速度上11MB/s也是小意思


------解决方案--------------------
彻底解决udp的丢包和乱序,需要有个流水号记录顺序和丢失状况,还有有超时重发机制,这个超时重发通常又是重发全部而不是一个包,因为你不知道哪个包丢了,让对端告诉你的话这个“通知”一样可能丢

如果udp没开校验和,还需要加个crc之类的保证没有电磁干扰造成的错误

最后就相当于自己实现tcp,开销往往比直接用tcp还大

所以udp只适合不怕丢包,不怕乱序的场合
------解决方案--------------------
探讨

引用:
彻底解决udp的丢包和乱序,需要有个流水号记录顺序和丢失状况,还有有超时重发机制,这个超时重发通常又是重发全部而不是一个包,因为你不知道哪个包丢了,让对端告诉你的话这个“通知”一样可能丢

如果udp没开校验和,还需要加个crc之类的保证没有电磁干扰造成的错误

最后就相当于自己实现tcp,开销往往比直接用tcp还大

所以udp只适合不怕丢包,不怕乱序……

------解决方案--------------------


那就超出鄙人经验范围了,因为我很清楚udp的问题,从不会拿他发大文件

不值得为错误的设计和技术选型浪费太多心思,我觉得一个靠谱的团队leader应该能听进去正确意见
------解决方案--------------------
UDP要考虑的问题很多,除非是影音传输,不考虑丢包的场合,否则不建议使用,虽然QQ用它来传文件,但是人家技术力量雄厚,不是一般公司可以效仿的,这个UDP真要实现可靠传输,要考虑的问题很多,首先那个丢包重传要自己考虑,而TCP则是自动重传的。但这还不是最难的,最难的是速度控制,TCP有个自动控速算法,而UDP则没有,必须自己实现,否则当你的发送速度远大于对方的接收能力或大于网络传输能力,将导致大量丢包,重传都来不及,恶性循环。
自动控速算法要实现非常难,你可以参考下TCP的实现方式,由于发送方不知道带宽有多大,因此发送速度不能实现设定好,要在发送过程中实时调整速度已达到最大化利用带宽。
至于你说的可以自动拆包?我反正超过1500的情况都传输失败,100%丢包。即使可以又如何?如果要控制速度,丢包重传,你不自己拆包又如何控制?
------解决方案--------------------
不用纠结了,这个问题解决难度极大,基本不太可能

还有一个小概率事件您考虑过没有,如果人家不是“以太网”呢,如果您做的是个客户端程序的话,难保人家不在电话拨号的机器上用

我和qldsrx简单地说1500字节的udp限制其实都不准确,这东西是MTU,链路层的概念,以太网是物理层的,如果换其他的物理层,MTU就不见得1500,大的有64K的,小的有几百字节的,速度也是千差万别。

当然,一个好的可靠性传输机制能解决这种问题,但我要说的是,即便你搞出来一个局域以太网上能基本稳定的udp传输,广域网不记得靠谱、无线网不见的靠谱,非以太网也不见得靠谱