【计算机网络】TCP粘包

转载自:https://blog.csdn.net/weixin_41047704/article/details/85340311

在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接。在TCP的socket编程中,发送端和接收端都有成对的socket。发送端为了将多个发往接收端的包,更加高效的的发给接收端,于是采用了优化算法(Nagle算法),将多次间隔较小、数据量较小的数据,合并成一个数据量大的数据块,然后进行封包。那么这样一来,接收端就必须使用高效科学的拆包机制来分辨这些数据。

1.Q:什么是TCP粘包问题?

TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出现粘包的原因是多方面的,可能是来自发送方,也可能是来自接收方。

2.Q:造成TCP粘包的原因

(1)发送方原因

TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:

  1. 只有上一个分组得到确认,才会发送下一个分组
  2. 收集多个小分组,在一个确认到来时一起发送

Nagle算法造成了发送方可能会出现粘包问题

(2)接收方原因

TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

3.Q:什么时候需要处理粘包现象?

  1. 如果发送方发送的多组数据本来就是同一块数据的不同部分,比如说一个文件被分成多个部分发送,这时当然不需要处理粘包现象
  2. 如果多个分组毫不相干,甚至是并列关系,那么这个时候就一定要处理粘包现象了

4.Q:如何处理粘包现象?

(1)发送方

对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。

(2)接收方

接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。

(2)应用层

应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。

解决办法:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,但是如何判断每条数据的长度呢?

  1. 格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
  2. 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。

5.Q:UDP会不会产生粘包问题呢?

TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。

UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。

举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
TCP粘包是指发送方发送的多个数据包在传输过程中被合并成一个数据包,接收方接收到的数据包也是合并后的数据包。C++ TCP粘包的原因是TCP协议的Nagle算法和接收方缓冲区的大小限制。Nagle算法会将小的数据包合并成一个大的数据包发送,而接收方缓冲区的大小限制会导致接收方无法及时处理接收到的数据包,从而造成粘包现象。 解决C++ TCP粘包的方法有以下几种: 1.设置TCP_NODELAY选项,禁用Nagle算法,使得每个数据包都立即发送。 2.在发送的数据包中添加特殊的分隔符,接收方根据分隔符将数据包拆分成多个数据包。 3.在发送的数据包中添加数据包长度信息,接收方根据长度信息将数据包拆分成多个数据包。 以下是C++ TCP粘包的解决方法示例: 1.禁用Nagle算法 ```c++ int flag = 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); ``` 2.添加分隔符 ```c++ // 发送端 string msg = "hello|world|"; send(sockfd, msg.c_str(), msg.size(), 0); // 接收端 char buf[MAXSIZE]; string data; while (true) { int len = recv(sockfd, buf, MAXSIZE, 0); if (len <= 0) { break; } data += string(buf, len); while (true) { int pos = data.find("|"); if (pos == string::npos) { break; } string msg = data.substr(0, pos); data = data.substr(pos + 1); // 处理接收到的数据包 } } ``` 3.添加数据包长度信息 ```c++ // 发送端 string msg = "hello world"; int len = msg.size(); send(sockfd, (char *)&len, sizeof(len), 0); send(sockfd, msg.c_str(), msg.size(), 0); // 接收端 char buf[MAXSIZE]; int len; while (true) { int ret = recv(sockfd, (char *)&len, sizeof(len), 0); if (ret <= 0) { break; } int n = 0; while (n < len) { int len1 = min(MAXSIZE, len - n); int ret1 = recv(sockfd, buf, len1, 0); if (ret1 <= 0) { break; } n += ret1; // 处理接收到的数据包 } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值