tcp接收策略

策略2种:

1. 首先,接收一个长度。然后,根据长度再继续接收数据。(感觉要好一点)

2. 直接接收所有,再判断长度




方法1 : 先收长度,再收数据(推荐)

最近在调程序的时候,发现发送端发送一个119136个char的内存的时候,在接收端不能全部接收,

于是,通过调试发现,必须在接收端多次的recv以后,进行拼接:

代码如下:

char Lenbuf[4];
 int iLen; 
 //接收数据
 int bytes;
 //先接受前面的四位消息体长度
 
 if((bytes=recv(clientSocket,Lenbuf,sizeof(Lenbuf),0))==SOCKET_ERROR)
 {
  int iErrNo = WSAGetLastError();
  printf("接收数据失败! clientLoc:%d,socket:%d, errono : %d \n",
    m_iClientLoc ,clientSocket,iErrNo  );   
  if ( WSAECONNRESET == iErrNo  )
  {
   return -1;   
  }
  else
  {
   return 0;
  
 }
 //这个长度包含了前面接收的4个byte的长度
 //用于放内容的长度
 memcpy( &iLen, Lenbuf, sizeof(iLen) );
 char *buf=(char *)malloc((iLen+1)*sizeof(char));
 //第二次接收的话,就从下面的buf开始了,已经抛掉了buf长度的
 //如果数据包很大的话,需要多次recv
 int irecv =0;
 if((bytes=recv(clientSocket,buf,(iLen+1),0))==SOCKET_ERROR)
 {
  printf("接收数据失败!\n");
  //exit(-1);
  return -1;
 }
 else
 {
  irecv += bytes;
  while ( irecv < (iLen-4) )
  {
   bytes = recv( clientSocket, buf+irecv,(iLen+1-irecv),0);
   if ( bytes == SOCKET_ERROR )
   {
    return -1;

   }
   else
   {
    irecv += bytes;
   }
  }

 }


方法2:一次接收,在判断长度

在每个 报头上说明包体的长度,这样通过报头大小来获取相应的数据大小;
	在此主要讨论解决方法3,在报头说明包体大小的方式
	在此方式中,每个包都分为两部分,第一部分为数据部分的大小,紧接的部分是数据部分,可以用结构体实现
	struct packet{
		unsigned int m_msgLen;
		char             data[maxsize];
	}
	这样数据在发送的过程中将报头和包体全部发送,在接收的时候先接收4字节的包头数据
	int a;	
	recv(sockfd,&a,4,0);
	或者recv(sockfd,buff,maxsize,0);//线将缓冲区数据全部拿出来再处理
	strncpy(&a,buff,4);
	这样就可以获取包体数据大小,然后通过报头大小获取包体数据来解决粘包


==============================================

代码:

粘包解决方案二:使用结构体,显式说明数据部分的长度

在这个方案中,我们需要定义一个‘struct packet’包结构,结构中指明数据部分的长度,用四个字节来表示。发送端的对等方接收报文时,先读取前四个字节,获取数据的长度,由长度来进行数据的读取。定义一个结构体

struct packet
{
        unsigned int msgLen ;  //4个字节字段,说明数据部分的大小
        char data[512] ;  //数据部分 
}

读写过程如下所示,这里抽取关键代码进行说明:

//发送数据过程
    struct packet writebuf;
    memset(&writebuf,0,sizeof(writebuf));
    while(fgets(writebuf.data,sizeof(writebuf.data),stdin)!=NULL)
    {      
            int n = strlen(writebuf.data);   //计算要发送的数据的字节数
            writebuf.msgLen =htonl(n);    //将该字节数保存在msgLen字段,注意字节序的转换
            writen(conn,&writebuf,4+n);   //发送数据,数据长度为4个字节的msgLen 加上data长度
            memset(&writebuf,0,sizeof(writebuf)); 
    }

下面是读取数据的过程,先读取msgLen字段,该字段指示了有效数据data的长度。依据该字段再读出data。

  memset(&readbuf,0,sizeof(readbuf));
  int ret = readn(conn,&readbuf.msgLen,4); //先读取四个字节,确定后续数据的长度
  if(ret == -1)
  {
           err_exit("readn");
  }
  else if(ret == 0)
 {
           printf("peer close\n");
           break;
}
 int dataBytes = ntohl(readbuf.msgLen); //字节序的转换
 int readBytes = readn(conn,readbuf.data,dataBytes); //读取出后续的数据
 if(readBytes == 0)
 {
         printf("peer close\n");
         break;
 }
 if(readBytes<0)
 {
          err_exit("read");
}





----

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值