TCP 粘包解决

TCP 粘包解决TCP 粘包 什么是粘包现象 TCP 粘包是指发送方发送的若干包数据到接收方接收时粘成一包 从接收缓冲区看 后一包数据的头紧接着前一包数据的尾 为什么出现粘包现象 1 发送方原因我们知道 TCP 默认会使用 Nagle 算法 而 Nagle 算法主要做两件事 1 只有上一个分组得到确认 才会发送下一个分组 2 收集多个小分组 在一个确认到来时一

TCP 粘包:
什么是粘包现象 :
  TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾




 

为什么出现粘包现象 :
(1) 发送方原因   我们知道,TCP默认会使用Nagle算法。而Nagle算法主要做两件事:
         1)只有上一个分组得到确认,才会发送下一个分组;
         2)收集多个小分组,在一个确认到来时一起发送。所以,正是Nagle算法造成了发送方有可能造成粘包现象。                               由于TCP协议本身的机制(面向连接的可靠地协议-三次握手机制)客户端与服务器会维持一个连接(Channel),数据在连接不断开的情况下,可以持续不断地将多个数据包发往服务器,但是如果发送的网络数据包太小,那么他本身会启用Nagle算法(可配置是否启用)对较小的数据包进行合并(基于此,TCP的网络延迟要UDP的高些)然后再发送(超时或者包大小足够)。那么这样的话,服务器在接收到消息(数据流)的时候就无法区分哪些数据包是客户端自己分开发送的,这样产生了粘包;服务器在接收到数据库后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现象(确切来讲,对于基于TCP协议的应用,不应用包来描述,而应用流来描述),个人认为服务器接收端产生的粘包应该与linux内核处理socket的方式select轮询机制的线性扫描频度无关。
(2) 接收方原因    TCP接收到分组时,并不会立刻送至应用层处理,或者说,应用层并不一定会立即处理;实际上,TCP将收到的分组保存至接收缓存里,然后应用程序主动从缓存里读收到的分组。这样一来,如果TCP接收分组的速度大于应用程序读分组的速度多个包就会被存至缓存,应用程序读时,就会读到多个首尾相接粘到一起的包








什么时候需要处理粘包现象 :
(1) 如果发送方发送的多个分组本来就是同一个数据的不同部分,比如一个很大的文件被分成多个分组发送,这时,当然不需要处理粘包的现象;
(2) 但如果多个分组本毫不相干,甚至是并列的关系,我们就一定要处理粘包问题了。比如,我当时要接收的每个分组都是一个有固定格式的商品信息,如果不处理粘包问题,每个读进来的分组我只会处理最前边的那个商品,后边的就会被丢弃。这显然不是我要的结果。




如何处理粘包现象 :
(1) 发送方
对于发送方造成的粘包现象,我们可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭Nagle算法。
(2) 接收方
遗憾的是TCP并没有处理接收方粘包现象的机制,我们只能在应用层进行处理
(3) 应用层处理
应用层的处理简单易行!并且不仅可以解决接收方造成的粘包问题,还能解决发送方造成的粘包问题。
解决方法就是循环处理:应用程序在处理从缓存读来的分组时,读完一条数据时,就应该循环读下一条数据,直到所有的数据都被处理;但是如何判断每条数据的长度呢?                                                                                                                                             解决方法:
       1、关闭TCP套接字的NAGL的算法(效率低下)
       2、每发送一次,等待对方收到才发送下一条。(效率低下)            
       3、回答机制:
       4、在数据之前添加特殊信号长度
             [ 5 ] abcefg
             [13] 23


























   解决TCP粘包的服务器的代码实现 : 

#include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        //服务器 int main() { //1创建套接字 int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket fail"); return -1; } socklen_t slen=sizeof(socklen_t); if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&slen,sizeof(slen))<0) { perror("setsockopt fail"); return -1; } //2绑定 /*先填充 再绑定bind*/ struct sockaddr_in myaddr; bzero(&myaddr,sizeof(myaddr)); myaddr.sin_family =AF_INET; myaddr.sin_port =htons(7979); myaddr.sin_addr.s_addr =INADDR_ANY; if(bind(sock,(struct sockaddr*)&myaddr,sizeof(myaddr))<0) { perror("bind fail"); return -1; } //3监听 if(listen(sock,1)<0) { perror("listen fail"); return -1; } int newsock=accept(sock,NULL,NULL); //sleep(5);//在10秒的睡眠时,客户分三次发送1 2 3字符串。 char buf[100]=""; int ilen=0; short num=0; int total=0; char* p=NULL; while(1) { total=0; p=(char*)# bzero(buf,sizeof(buf)); //读取长度   [5] abcefg 相当于读取的括号中的 5 while(total<2)//1 2 { ilen=recv(newsock,p+total,1,0); total+=ilen; } //读取字符内容 [5] abcefg 相当于读取 abcefg 的数据 total=0; while(total 
        
       
      
     
    
  

   服务器读取内容的效果 , 完整的将客户端发送过来的数据进行了读取 :

TCP 粘包解决

   客户端发送数据的代码 : 

#include 
  
    #include 
   
     #include 
    
      #include 
     
       #include 
      
        //服务器 int main() { //1创建 int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("fail\n"); return -1; } //2绑定 //3发送连接 struct sockaddr_in myaddr; bzero(&myaddr,sizeof(myaddr)); myaddr.sin_family =AF_INET; myaddr.sin_port =htons(7979); myaddr.sin_addr.s_addr =inet_addr("127.0.0.1"); if(connect(sock,(struct sockaddr*)&myaddr,sizeof(myaddr))<0) { perror("连接失败"); return -1; } //4发送信息 short ilen=5; //每次发送数据前先发送数据长度 如:  [5] abcefg send(sock,"12345",5,0); ilen=17; send(sock,&ilen,2,0); send(sock,"abcdefghijklmnopq",17,0); ilen=12; send(sock,&ilen,2,0); send(sock,"How are you!",12,0); sleep(5); //3关闭 close(sock); } 
       
      
     
    
  

   我们再来看看现象 : 

TCP 粘包解决

结果将数据完好的读取了出来 , 是因为一个字节发送 , 一个字节读取的所以没有出现粘包现象 :

TCP 粘包解决

我们再来看看粘包的现象  ,  这次发送过来的是结构体数据 , 而服务器需要读取结构体的数据 .

TCP 粘包解决

结果 出现粘包现象 :

TCP 粘包解决

  怎么解决上面出现的粘包现象 , 只需要将客户端发送的数据 sleep(2) , 再把服务器的 sleep(5)注释掉 , 解决了 , 每次等发送一个结构体就等待 , 直到发送完毕再发送第二个结构体 , 就没有出现粘包现象 .

TCP 粘包解决

结果 数据完好的读取了出来 :

TCP 粘包解决

TCP 粘包解决
最美的不是下雨天,是曾与你躲过雨的屋檐。《不能说的秘密》

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220898.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 下午7:24
下一篇 2026年3月17日 下午7:24


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号