TCP通信处理粘包详解

通常所谓的TCP粘包是在一次接收数据不能彻底地体现一个完整的消息数据。TCP通信为什么存在粘包呢?主要缘由是TCP是以流的方式来处理数据,再加上网络上MTU的每每小于在应用处理的消息数据,因此就会引起一次接收的数据没法知足消息的须要,致使粘包的存在。处理粘包的惟一方法就是制定应用层的数据通信协议,经过协议来规范现有接收的数据是否知足消息数据的须要。在应用中处理粘包的基础方法主要有两种分别是以4节字描述消息大小或以结束符,实际上也有二者相结合的如HTTP,redis的通信协议等。redis

在平时交流过程发现一些朋友即便作了这些协议的处理,但有时在处理数据的时候也会出现数据不对的状况。这主要缘由他们在一些个别状况下没有处理好。由于当一系列的消息发送过来的时候,对于4节字头或结束符分布位置都是不肯定的。一种简单的状况就是当前消息处理完成后,紧接着就是处理一下个消息的4节字描述,但在实际状况下当前接收的buffer剩下的内容有可能不足4节字的。若是你想经过通信的程序来测这状况相对来讲触发的机率性不高,因此对于协议分析的功能最好经过单元测试来模拟。网络

经过下面这个图能够更清晰地了解协议标记数据分布的状况 单元测试

下面简单地介绍一下4字节描述大小和结束符和处理方式。测试

     4字节大小描述方式

 1         public void Import(byte[] data, int start, int count)
 2         {
 3             while (count > 0)
 4             {
 5                 if (!mLoading)
 6                 {
 7                     mCheckSize.Reset();
 8                     mStream.SetLength(0);
 9                     mStream.Position = 0;
10                     mLoading = true;
11                 }
12                 if (mCheckSize.Length == -1)
13                 {
14                     while (count > 0 && mCheckSize.Length == -1)
15                     {
16                         mCheckSize.Import(data[start]);
17                         start++;
18                         count--;
19                     }
20                 }
21                 else
22                 {
23                     if (OnImport(data, ref start, ref count))
24                     {
25                         mLoading = false;
26                         if (Receive != null)
27                         {
28                             mStream.Position = 0;
29                             Receive(mStream);
30                         }
31                     }
32                 }
33             }
34         }
35 
36 
37         public void Import(byte value)
38         {
39             LengthData[mIndex] = value;
40             if (mIndex == 3)
41             {
42                 Length = BitConverter.ToInt32(LengthData, 0);
43                 if (!LittleEndian)
44                     Length = Endian.SwapInt32(Length);
45             }
46             else
47             {
48                 mIndex++;
49             }
50         }

     代码很简单若是没有长度描述的状况就把数据导入到消息长度描述的buffer中,若是当前buffer知足4位的状况直接获得相应长度。后面的工做就是获取相应长度的buffer便可。spa

    结束符方式     

 1         public void Import(byte[] data, int start, int count)
 2         {
 3             while (count > 0)
 4             {
 5                 if (!mLoading)
 6                 {
 7                     mStream.SetLength(0);
 8                     mStream.Position = 0;
 9                     mLoading = true;
10                 }
11                 if (data[x] == mEof[0])
12                 {
13                     start += mEof.Length;
14                     count -= mEof.Length;
15                     mLoading = false;
16                     if (Receive != null)
17                     {
18                         mStream.Position = 0;
19                         Receive(mStream);
20                     }
21                 }
22                 else
23                 {
24                     mStream.Write(data[start]);
25                     start++;
26                     count--;
27                 }
28             }
29         }

    结束符的处理方式就相对来讲简单多了。.net

    以上就是两种TCP数据处理粘包的状况,相关代码紧供参考。code