Qt的TCP粘包分包

 

        粘包只可能出现在流传输中,TCP是基于流传输的,而UDP是不会出现粘包,因为UDP是基于报文的,也就是说UDP发送端调用几次write,接收端必须调用相同次数的read读完,每次最多只能读取一个报文,报文与报文是不会合并的,如果缓冲区小于报文长度,则多出来的部分会被丢掉。TCP不同了,它会合并消息,并且以不确定方式合并,这样就需要我们去粘包处理了,TCP造成粘包可能是发送端也可能是接收端的原因。主要表现为收到的包不完整或者收到的包带着其它包的全部或部分数据。但数据还是按顺序的,不会出现包之间顺序混合,只需要找到包的开头和结尾进行组合就好了。

 

考虑粘包问题的情况

1、如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题(因为只有一种包结构,类似于http协议)。关闭连接主要要双方都发送close连接(参考tcp关闭协议)。如:A需要发送一段字符串给B,那么A与B建立连接,然后发送双方都默认好的协议字符如"hello give me your message",然后B收到报文后,就将缓冲区数据接收,然后关闭连接,这样粘包问题不用考虑到,因为大家都知道是发送一段字符。

2、如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包。

3、如果双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构:

   a、"hello give me your message"

   b、"Don't give me your message"

     这样的话,如果发送方连续发送两个这样的包出去,接收方一次接收可能会是"hello give me abour your messageDon't give me  abour your message",这样接收方就傻眼了,到底应该怎么分了?因为没有协议规定怎么拆分这段字符串,所以要处理好分包,需要双方组织一个比较好的包结构,一般会在头上加上消息类型,消息长度等以确保正常接收。

 

总结:长连接且发多种数据格式的情况下需要考虑分包。
   

 

解决方法

1、消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即使粘包了通过接收方编程实现获取定长报文也能区分。

2、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分。

3、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段

4、添加包头和包尾分隔符,不使用数据大小来分包。

 

代码

这里采用的是在消息头部和尾部分别添加标志。

只有同时有包头和包尾的消息才是合法的。

//收到服务器的消息,分包处理,可以修改为设置包头和包尾的标志帧,而不是固定的$start$和$end$
//AnalysisData 处理一包数据的函数
void DataProcess::handleRecvData(QByteArray data)
{
//    qInfo()<<"tcp recv "<

 

你可能感兴趣的:(Qt)