什么叫“粘包”?
“粘包”指的是,分次发送的数据包,接收端接收一次数据,读到了对应发送端多于一次的数据。例如,发送端先发送了一个数据包
AAAA
后续又发送了一个数据包
BBBB
接收端在读取数据时,读取一次,收到的数据是
AAAABBBB
这就是某些情况下所说的粘包。
UDP通信会不会粘包?
对于UDP通信,理论上(未究其根源,根据搜索到的相关说法)不会发生粘包,因为UDP数据包各有自己的“边界”(同样的原因,姑且认为此说法成立)。
工程中遇到的UDP“粘包”
实践中,使用UDP通信过程中,还是遇到了“粘包”现象。就笔者所遇到的情况就是,代码中相邻两条指令,各发送一个UDP数据包,接收端得知有数据到达,读取一次,得到了发送端两次发送内容的拼合结果。
UDP“粘包”原因分析
就实践中遇到的UDP“粘包”问题,场景有二,一是Windows平台,接连发送两个UDP包;二是基于单片机的平台,利用串口发送数据到串口-网络转换模块,发送不同数据包间隔时间比较短的时候。
对于场景一,做了测试,在两条指令之间加延时,并不总是有效。可能跟系统底层网络传输实现细节或者应用程序与底层的衔接有关。
对于场景二,与串口-网络转换模块实现有关,(似乎)其实现中是定时将从串口获取的数据串以UDP数据包发送出去。如果两个数据包未被其识别为两个数据包(视实现的不同,也可能仅仅是定时将缓存的串口数据封包为UDP数据包发送出去而已),则作为一个更长的包发送出去(此种情况,串口数据包长度、串口波特率、串口-网络转换模块内部实现在某些组合下,还会发生“拆包”现象,此处不展开)。
UDP“粘包”的影响
UDP通信过程中,不论有意设计还是无意实现,都相当与存在一个事实上的“协议”。如果在发送和接收时,意识不到UDP“粘包”的可能,无意之中会基于“一发一收、不粘不拆”的前提。此时,在发生粘包时,如果是数据包定长且只做长度校验,则粘连的数据包会被认为是无效的数据包而丢弃,即参与粘连的数据包都成了无效包;如果只根据魔术字(magic number)之类的手段识别包头,则粘连的数据包只有位于开头位置的数据会被识别,后续的参与粘连的数据包都被丢弃了,形成事实上的无效包。无效包是否会影响系统的运作,影响程度如何,视系统敏感程度而异,不展开。
UDP“粘包”的克服
从发送端而言,对于场景一,如果加延时不保证总是有效,可以引入发送队列,另设定时或轮询机制,从队列中依次取数据、发数据。
从发送端而言,对于场景二,调整串口数据包长度、串口波特率等串口-网络转换模块参数,降低“粘包”(及“拆包”)发生的概率。
对于接收端而言,要正视事实上会发生“粘包”(及“拆包”)的现实,引入FIFO队列、环形缓冲等,依靠数据串内的逻辑边界而不是依赖接收数据包的天然边界来分割数据包。必要时辅以有助于上述手段实现的协议设计。
题后
从工程的角度,重在解决问题,其次才是辩论是非(此过程不排斥对底层实现的深层探索)。处理资源更丰富、程序实现更灵活、应变能力更强的一方,在不违背大原则的前提下(如技术要求等),最好多做些工作,来实现提高整体通信可靠性的目标。如果过多的按照条条杠杠“苛责”处理资源紧张、程序实现灵活度受限、应变能力偏弱的一方,最终结果可能就是整体通信可靠性既未达到本来可以达到的程度,也在陪弱势一方调试、测试、辩论过程中浪费掉很多的时间,牵扯到很多的精力,而这些时间精力如果用于强势的一方调整自己的实现去达到同样的目标,可能重复十次八次还有富余。