Benjamin

静以修身,俭以养德,非澹薄无以明志,非宁静无以致远。
随笔 - 397, 文章 - 0, 评论 - 196, 引用 - 0
数据加载中……

tcp中的粘包、半包的处理方法

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

  出现粘包现象的原因既可能由发送方造成,也可能由接收方造成。
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。
      接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
c++的解决方法如下:接收端
    int iRecvSize = PackteSize + 10;
    int iRet;
   int idx = 0;
   while (iRecvSize > 0)
   {
    iRet = recv(AcceptSocket, recvbuf+idx, iRecvSize, 0);
    if (iRet > 0)
    {
     idx += iRet;
     iRecvSize -= iRet;
    }
    else if (iRet == 0)
    {
     break;
    }
    else if ( iRet == SOCKET_ERROR)
    {
     break;
    }
   }
发送端:
   int iSendSize = PacketSize + 10;
   int iSent;
   int idx = 0;
   while (iSendSize > 0)
   {
    iSent = send(m_socket,sendbuffer+idx,iSendSize,0);
    if (iSent > 0)
    {
     idx += iSent;
     iSendSize -= iSent;
    }
    else if (iSent == 0)
    {
     break;
    }
    else if (iSent == SOCKET_ERROR)
    {
     wprintf(L"send failed with error: %d\n", WSAGetLastError());
     //closesocket(m_socket);
     //WSACleanup();
     break;
    }
   }
若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包),反之则必须分包。
c#中用socket的Available成员来表示是否还有数据需要读取,如果Available>0,表示还有数据需有读取,反之读取完成。

  public class ConnectInfo
    {
        public ArrayList tmpList { get; set; }
        public SocketAsyncEventArgs SendArg { get; set; }
        public SocketAsyncEventArgs ReceiveArg { get; set; }
        public Socket ServerSocket { get; set; }
        public User user = new User();

    }


     if (client.Available > 0)
      {
                    Console.WriteLine("粘包处理");
                    for (int i = 0; i < rec; i++)
                        info.tmpList.Add(datas[i]);
                    Array.Clear(datas, 0, datas.Length);

                    datas = new byte[client.Available];
                    e.SetBuffer(datas, 0, datas.Length);
                    client.ReceiveAsync(e);
                   
       }
       else
       {
                    //检查暂存数据的ArrayList中有没有数据,有就和本次的数据合并
                    if (info.tmpList.Count > 0)
                    {
                        for (int i = 0; i < rec; i++)
                            info.tmpList.Add(datas[i]);
                        datas = info.tmpList.ToArray(typeof(byte)) as byte[];
                        rec = datas.Length;
                    }

                    //对接收的完整数据进行处理
       }

半包顾名思义,就不是一个完整的包,tcp发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
常见的解决方法就是制定规范的数据传输格式。

posted on 2013-09-22 21:22 Benjamin 阅读(4949) 评论(0)  编辑 收藏 引用 所属分类: C/C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理