TCP 报文分包和粘包

1.分包和粘包

1.1 分包

  发送方发送字符串 “helloworld” ,接收方却接收到了两个字符串 “hello” 和 “world”。

1.2 粘包

  发送方发送两个字符串 “hello” 和 “world”,接收方却一次性接收到了 “helloworld”。

2.TCP是可靠传输

  可靠传输能保证数据无差错、无丢失、按序和无重复的交付。

  1)顺序不变,例如发送方发送hello,接收方也一定顺序接收到hello,这个是TCP协议承诺的,因此这点成为我们解决分包和粘包问题的关键.

  2) 分割的包中间不会插入其他数据。
在实际开发中,为了解决分包和粘包的问题,就一定要自定义一份协议,最常用的方法是:

  报文长度 + 报文内容

  其实这个方法就是,告诉系统,我发送的报文内容的长度是多少,你接收这些就行了。

  3)报文长度为 4 字节的整数,表示的是报文内容的长度,而不是整个 TCP 报文的长度,整个 TCP 报文的长度是报文内容的长度 +4

  报文长度是 4 字节的整数,即int ,是以二进制流的方式写入socket,而不是 ascii 码。

3.TcpWrite() 和 TcpRead()

  解决分包和粘包的方法,就封装在了这两个函数里面。下面是两个函数中的一些参数。

sockfd:可用的socket连接。buffer:接收数据缓冲区的地址。

ibuflen:本次成功接收数据的字节数。

itimeout:接收等待超时的时间,单位:秒,缺省值是0-无限等待。

返回值:true-成功; false-失败,失败有两种情况:1)等待超时;2) socket连接已不可用

3.1 TcpWrite()

(1)函数的声明及参数

bool TcpWrite(const int sockfd,const char *buffer,const int ibuflne);

从已经准备好的socket中读取数据。

sockfd:已经准备好的socket连接。

buffer:接收数据缓冲区的地址。

n:本次接收数据的字节数。

(2)函数的实现

bool TcpWrite(const int sockfd,const char *buffer,const int ibuflne)
{ 
  if (sockfd == -1) return false;
  fd_set tmpfd;
  FD_ZERO(&tmpfd) ;
  FD_SET(sockfd,&tmpfd) ;
  struct timeval timeout;
  timeout.tv_sec = 5 ; timeout.tv_usec= 0;
  if ( select(sockfd+1,0,&tmpfd,0,&timeout)<=0 ) return false;
  int ilen=0;
  
  //如果长度为0,就采用字符串的长度
  if (ibuflen==O)     ilen=strlen (buffer);
  elseilen=ibuflen;

  int ilenn=htonl(ilen);   //转换为网络字节序。

  char strTBuffer[ilen+4];
  memset(strTBuffer,0, sizeof(strTBuffer));   
  memcpy(strTBuffer,&ilenn,4);
  memcpy(strTBuffer+4, buffer, ilen) ;
  if(Writen(sockfd, strTBuffer,ilen+4)==false) return false;
  return true;

}


3.2 TcpRead()

  
(1)函数的声明及参数

bool TcpRead(const int sockfd,char *buffer,int *ibflen,const int itimeout);

向socket的对端发送数据。

sockfd:可用的socket连接。

buffer:待发送数据缓冲区的地址。

ibuflen:待发送数据的字节数,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。

返回值:true——成功;false——失败,如果失败,表示socket连接已经不能用。

(2)函数的实现

bool TcpRead(const int sockfd, char *buffer, int *ibuflen, const int itimeout)
{
  if (sockfd =-1)return false;
  if(itimeout > 0)
  fd_set tmpfd;
  FD_ZERO(&tmpfd) ;
  FD_SET(sockfd,&tmpfd);
  struct timeval timeout;
  timeout.tv_sec = itimeout ; timeout.tv_usec =0;
  int i ;
  if ( (i = select(sockfd+1,&tmpfd,0,0,&timeout))<=0 ) return false;
  
   (*ibuflen)= 0;
   
  if(Readn(sockfd,(char*)ibuflen, 4)==false) return false;
  (*ibuflen)=ntohl(*ibuflen);//把网络字节序转换为主机字节序。
  if (Readn(sockfd, buffer,(*ibuflen))== false)return false;
  return true;
  }


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
TCP和UDP在传输过程中都可能会出现分包粘包的问题,但是处理方式有所不同。 对于TCP协议,如果定义的TCP包没有超过范围,即在IP层不需要分包,传输过程中就可以避免IP层组包发生的错误。但是如果超过范围,即IP数据报大于1500字节,发送方IP层就需要将数据包分成若干片,而接收方IP层则需要进行数据报的重组。TCP协议保证了可靠传输,如果发生组包错误,该包会被重传,确保数据的可靠性。\[1\] 对于UDP协议,由于UDP发送时没有经过Negal算法优化,不会将多个小包合并一次发送出去,因此不存在粘包问题。在UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包,这样接收端应用程序一次recv只能从socket接收缓冲区中读出一个数据包。也就是说,发送端send了几次,接收端必须recv几次(无论recv时指定了多大的缓冲区)。因此,UDP协议不会出现粘包问题。\[2\] 总结来说,TCP协议在IP层可能会发生分包和组包的情况,但会保证可靠传输;而UDP协议不存在粘包问题,每个UDP包都是独立的。 #### 引用[.reference_title] - *1* *2* *3* [tcp、udp、分包粘包](https://blog.csdn.net/liao_hb/article/details/106382297)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值