Tcp协议发送数据,粘包,拆包问题,这句话本身是错误的,tcp是协议,发送接收数据时并不会粘包,tcp,ip,只负责传输数据 粘包是应用层解析数据时,包1和包2粘连在一起,解析数据时,出现错误的,网络中,很多博主,就没搞清楚。
send,发送频率太快,发送包的时候,有可能发送1400字节,或100个字节,按流的方式发送,应用层很快频率,非阻塞去recv数据时,不一定能读到1400个字节,可能只有1000个,下次再去读取时,读到应用层发送的第一包里的后400个字节开头的数据包,出现粘包,解析数据头是出错,其实tcp传输数据时,没有包的概念,和uart串口一样,只有流的概念,tcp协议讲的很清晰,tcp是面向流的,一个一个字节流, tcp传输时,只按流的方式发送,没有一包一包的概念,假如发送数据很大,tcp协议,自动分成多包一个一个字节进行发送,接收端接收到数据,会自动按包号,进行组合假如分成1,2,3,4,5包,接收端接收到1,2,4,5,底层协议自动组合,错误将自动重传,所以,tcp是可靠的传输。
这个包和应用层send发包不是一个概念,udp才是面向报文的,应用层按包发送接收,发送1400字节的包,udp只要网络正常,基本一定能收到1400个字节的包,tcp不一定能recv能接收到1400个字节,所以如果用结构,去解析包头,数据段的数据,这时候,有可能就回错误,包头校验不对,如下一段代码,用结构类型去解析接收buf的数据,注意,我这里qt的read接收的,linux是recv。
PT_udpSocketDataPkt _tDataPkt1 = (PT_udpSocketDataPkt)this->databufer2;
memset(_tDataPkt1, 0, sizeof(T_udpSocketDataPkt));
int rcvlen = ClientTcpSocket->read((char*)_tDataPkt1, UDP_MAX_LEN);
if(rcvlen <= 0)
{
QMessageBox::information(this, CCODE("提示"), CCODE("接收服务端数据失败!"));
}
else if ( ( (rcvlen >= UDP_MIN_LEN) && (rcvlen <= UDP_MAX_LEN) ) \
&& (_tDataPkt1->DataHead.u32Sync == PKT_SYNC) && (_tDataPkt1->DataHead.u8Cmd = CMD_SNAP_PIC_CMD_ACK))
{
if (_tDataPkt1->DataHead.u16TotalPktNum != _tDataPkt1->DataHead.u16CurrentPktSn)
{
}
}
如果服务端发数据够快,客服端读取数据时,就可能解析不对,也是被udp干扰了,写udp时,就是按包发送,接收,去解析的,没问题。 放到tcp就有可能有问题。 csdn很多博主,在解决粘包问题时,其中解决方法,是这样建议的,没有考虑这个问题,什么极端情况都可能出现的。
tcp应用层解析数据时,发送大量数据,很有可能会出现这样情况,上面的方法其实也可以在某系业务时可用,如发送频率较慢,几ms发送数据,接收端慢一点,能及时处理读取全部数据,并且回复发送端,收到已处理消息,接收端再发送,就可以这样,但是发送数据很慢,传输一个几mb的文件,很慢,这样用udp协议也可以了,就没必要用tcp了,tcp就是可靠。
如果是传输大文件时,只需发送方告诉接收方,文件的大小,接收按流的方式读取到buf里,recv多少个字节,就放到buf里,直到收完大小为止,后者,很多人建议的。 如果事先不知道文件大小,也可以这样做,在数据里,加结束标志如\\\,读到包结束标志后,表示完整包发送完毕。
需要处理大量数据时,最好还是按流的方式处理数据,放到buf里,如果数据是报文,消息,可以加标志位,应用层根据结束标志位,在流buf里,寻找一个完整的包,如果是连续的文件,接收多少个字节,就按多少个字节拼接到buf里。
|