tcp接收策略

来源:互联网 发布:asp.net商城系统源码 编辑:程序博客网 时间:2024/05/29 03:40

策略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");}





----