Socket 应用开发中,还有一个话题是讨论的比较多的,那就是数据接收后如何处理的问题。这也是一个令刚接触 Socket 开发的人很头疼的问题。

因为SocketTCP通讯中有一个“粘包”的现象,既:大多数时候发送端多次发送的小数据包会被连在一起被接收端同时接收到,多个小包被组成一个大包被接收。有时候一个大数据包又会被拆成多个小数据包发送。这样就存在一个将数据包拆分和重新组合的问题。那么如何去处理这个问题呢?这就是我今天要讲的通讯协议。

所谓的协议就是通讯双方协商并制定好要传送的数据的结构与格式。并按制定好的格式去组合与分析数据。从而使数据得以被准确的理解和处理。

那么我们如何去制定通讯协议呢?很简单,就是指定数据中各个字节所代表的意义。比如说:第一位代表封包头,第二位代表封类型,第三、四位代表封包的数据长度。然后后面是实际的数据内容。

如下面这个例子:

01

01

06 00

01 0f ef 87 56 34

协议类别

协议代码

数据长度

实际数据

前面三部分称之为封包头,它的长度是固定的,第四部分是封包数据,它的长度是不固定的,由第三部分标识其长度。因为我们的协议将用在TCP中,所以我没有加入校验位。原因是TCP可以保证数据的完整性。校验位是没有必要存在的。

接下来我们要为这个数据封包声明一个类来封装它:

Socket开发之通讯协议及处理_class
 1Socket开发之通讯协议及处理_通讯_02    public class Message
 2Socket开发之通讯协议及处理_buffer_03    {
 3Socket开发之通讯协议及处理_socket_04        private byte _class;
 4Socket开发之通讯协议及处理_socket_04        private byte _flag;
 5Socket开发之通讯协议及处理_socket_04        private int _size;
 6Socket开发之通讯协议及处理_socket_04        private byte[] _content;
 7Socket开发之通讯协议及处理_socket_04
 8Socket开发之通讯协议及处理_socket_04        public byte[] Content
 9Socket开发之通讯协议及处理_socket_10        {
10Socket开发之通讯协议及处理_socket_10            get return _content; }
11Socket开发之通讯协议及处理_socket_10            set { _content = value; }
12Socket开发之通讯协议及处理_socket_13        }

13Socket开发之通讯协议及处理_socket_04
14Socket开发之通讯协议及处理_socket_04        public int Size
15Socket开发之通讯协议及处理_socket_10        {
16Socket开发之通讯协议及处理_socket_10            get return _size; }
17Socket开发之通讯协议及处理_socket_10            set { _size = value; }
18Socket开发之通讯协议及处理_socket_13        }

19Socket开发之通讯协议及处理_socket_04
20Socket开发之通讯协议及处理_socket_04        public byte Flag
21Socket开发之通讯协议及处理_socket_10        {
22Socket开发之通讯协议及处理_socket_10            get return _flag; }
23Socket开发之通讯协议及处理_socket_10            set { _flag = value; }
24Socket开发之通讯协议及处理_socket_13        }

25Socket开发之通讯协议及处理_socket_04
26Socket开发之通讯协议及处理_socket_04        public byte Class
27Socket开发之通讯协议及处理_socket_10        {
28Socket开发之通讯协议及处理_socket_10            get return _class; }
29Socket开发之通讯协议及处理_socket_10            set { _class = value; }
30Socket开发之通讯协议及处理_socket_13        }

31Socket开发之通讯协议及处理_socket_04
32Socket开发之通讯协议及处理_socket_04        public Message()
33Socket开发之通讯协议及处理_socket_10        {
34Socket开发之通讯协议及处理_socket_04
35Socket开发之通讯协议及处理_socket_13        }

36Socket开发之通讯协议及处理_socket_04
37Socket开发之通讯协议及处理_socket_04        public Message(byte @class, byte flag, byte[] content)
38Socket开发之通讯协议及处理_socket_10        {
39Socket开发之通讯协议及处理_socket_04            _class = @class;
40Socket开发之通讯协议及处理_socket_04            _flag = flag;
41Socket开发之通讯协议及处理_socket_04            _size = content.Length;
42Socket开发之通讯协议及处理_socket_04            _content = content;
43Socket开发之通讯协议及处理_socket_13        }

44Socket开发之通讯协议及处理_socket_04
45Socket开发之通讯协议及处理_socket_04        public byte[] ToBytes()
46Socket开发之通讯协议及处理_socket_10        {
47Socket开发之通讯协议及处理_socket_04            byte[] _byte;
48Socket开发之通讯协议及处理_socket_04            using (MemoryStream mem = new MemoryStream())
49Socket开发之通讯协议及处理_socket_10            {
50Socket开发之通讯协议及处理_socket_04                BinaryWriter writer = new BinaryWriter(mem);
51Socket开发之通讯协议及处理_socket_04                writer.Write(_class);
52Socket开发之通讯协议及处理_socket_04                writer.Write(_flag);
53Socket开发之通讯协议及处理_socket_04                writer.Write(_size);
54Socket开发之通讯协议及处理_socket_04                if (_size > 0)
55Socket开发之通讯协议及处理_socket_10                {
56Socket开发之通讯协议及处理_socket_04                    writer.Write(_content);
57Socket开发之通讯协议及处理_socket_13                }

58Socket开发之通讯协议及处理_socket_04                _byte = mem.ToArray();
59Socket开发之通讯协议及处理_socket_04                writer.Close();
60Socket开发之通讯协议及处理_socket_13            }

61Socket开发之通讯协议及处理_socket_04            return _byte;
62Socket开发之通讯协议及处理_socket_13        }

63Socket开发之通讯协议及处理_socket_04
64Socket开发之通讯协议及处理_socket_04        public static Message FromBytes(byte[] Buffer)
65Socket开发之通讯协议及处理_socket_10        {
66Socket开发之通讯协议及处理_socket_04            Message message = new Message();
67Socket开发之通讯协议及处理_socket_04            using (MemoryStream mem = new MemoryStream(Buffer))
68Socket开发之通讯协议及处理_socket_10            {
69Socket开发之通讯协议及处理_socket_04                BinaryReader reader = new BinaryReader(mem);
70Socket开发之通讯协议及处理_socket_04                message._class = reader.ReadByte();
71Socket开发之通讯协议及处理_socket_04                message._flag = reader.ReadByte();
72Socket开发之通讯协议及处理_socket_04                message._size = reader.ReadInt32();
73Socket开发之通讯协议及处理_socket_04                if (message._size > 0)
74Socket开发之通讯协议及处理_socket_10                {
75Socket开发之通讯协议及处理_socket_04                    message._content = reader.ReadBytes(message._size);
76Socket开发之通讯协议及处理_socket_13                }

77Socket开发之通讯协议及处理_socket_04                reader.Close();
78Socket开发之通讯协议及处理_socket_13            }

79Socket开发之通讯协议及处理_socket_04            return message;
80Socket开发之通讯协议及处理_socket_13        }

81Socket开发之通讯协议及处理_socket_04
82Socket开发之通讯协议及处理_buffer_83    }
 

我们可以用Tobytes()FromBytes()将封包转换成二进制数组和从二进制数组转换回来。

事情看起来已经解决了,但……真的是这样子吗?不然,我们知道,TCP数据是以流的形式被传送的,我们并不知道一个数据包是否被传送完毕,也不知道我们接收回来的数据包中是否有多个数据包,如果直接使用FromBytes()来转换的话,很可能会因为数据不完整而出现异常,也有可能会因为数据中含有多个数据包而导致数据丢失(因为你并不知道这些数据中含有多少个数据包)。那我们该怎么办?这也不难,我们先把接收回来的数据写入一个流中。然后分析其中是否有完整的数据包,如果有,将其从流中取出,并将这部分数据从流中清除。直到流中没有完整的数据为止,以后接收回来的数据就将其写入流的结尾处,并从头继续分析。直到结束。

让我们来看看这部分的代码:
Socket开发之通讯协议及处理_class
  1Socket开发之通讯协议及处理_通讯_02    public class MessageStream
  2Socket开发之通讯协议及处理_buffer_03    {
  3Socket开发之通讯协议及处理_socket_04        private byte[] _buffer;
  4Socket开发之通讯协议及处理_socket_04        private int _position;
  5Socket开发之通讯协议及处理_socket_04        private int _length;
  6Socket开发之通讯协议及处理_socket_04        private int _capacity;
  7Socket开发之通讯协议及处理_socket_04
  8Socket开发之通讯协议及处理_socket_04        public MessageStream()
  9Socket开发之通讯协议及处理_socket_10        {
 10Socket开发之通讯协议及处理_socket_04            _buffer = new byte[0];
 11Socket开发之通讯协议及处理_socket_04            _position = 0;
 12Socket开发之通讯协议及处理_socket_04            _length = 0;
 13Socket开发之通讯协议及处理_socket_04            _capacity = 0;
 14Socket开发之通讯协议及处理_socket_13        }

 15Socket开发之通讯协议及处理_socket_04
 16Socket开发之通讯协议及处理_socket_04        private byte ReadByte()
 17Socket开发之通讯协议及处理_socket_10        {
 18Socket开发之通讯协议及处理_socket_04            if (this._position >= this._length)
 19Socket开发之通讯协议及处理_socket_10            {
 20Socket开发之通讯协议及处理_socket_04                return 0;
 21Socket开发之通讯协议及处理_socket_13            }

 22Socket开发之通讯协议及处理_socket_04            return this._buffer[this._position++];
 23Socket开发之通讯协议及处理_socket_13        }

 24Socket开发之通讯协议及处理_socket_04
 25Socket开发之通讯协议及处理_socket_04        private int ReadInt()
 26Socket开发之通讯协议及处理_socket_10        {
 27Socket开发之通讯协议及处理_socket_04            int num = this._position += 4;
 28Socket开发之通讯协议及处理_socket_04            if (num > this._length)
 29Socket开发之通讯协议及处理_socket_10            {
 30Socket开发之通讯协议及处理_socket_04                this._position = this._length;
 31Socket开发之通讯协议及处理_socket_04                return -1;
 32Socket开发之通讯协议及处理_socket_13            }

 33Socket开发之通讯协议及处理_socket_04            return (((this._buffer[num - 4| (this._buffer[num - 3<< 8)) | (this._buffer[num - 2<< 0x10)) | (this._buffer[num - 1<< 0x18));
 34Socket开发之通讯协议及处理_socket_13        }

 35Socket开发之通讯协议及处理_socket_04
 36Socket开发之通讯协议及处理_socket_04        private byte[] ReadBytes(int count)
 37Socket开发之通讯协议及处理_socket_10        {
 38Socket开发之通讯协议及处理_socket_04            int num = this._length - this._position;
 39Socket开发之通讯协议及处理_socket_04            if (num > count)
 40Socket开发之通讯协议及处理_socket_10            {
 41Socket开发之通讯协议及处理_socket_04                num = count;
 42Socket开发之通讯协议及处理_socket_13            }

 43Socket开发之通讯协议及处理_socket_04            if (num <= 0)
 44Socket开发之通讯协议及处理_socket_10            {
 45Socket开发之通讯协议及处理_socket_04                return null;
 46Socket开发之通讯协议及处理_socket_13            }

 47Socket开发之通讯协议及处理_socket_04            byte[] buffer = new byte[num];
 48Socket开发之通讯协议及处理_socket_04            if (num <= 8)
 49Socket开发之通讯协议及处理_socket_10            {
 50Socket开发之通讯协议及处理_socket_04                int num2 = num;
 51Socket开发之通讯协议及处理_socket_04                while (--num2 >= 0)
 52Socket开发之通讯协议及处理_socket_10                {
 53Socket开发之通讯协议及处理_socket_04                    buffer[num2] = this._buffer[this._position + num2];
 54Socket开发之通讯协议及处理_socket_13                }

 55Socket开发之通讯协议及处理_socket_13            }

 56Socket开发之通讯协议及处理_socket_04            else
 57Socket开发之通讯协议及处理_socket_10            {
 58Socket开发之通讯协议及处理_socket_04                Buffer.BlockCopy(this._buffer, this._position, buffer, 0, num);
 59Socket开发之通讯协议及处理_socket_13            }

 60Socket开发之通讯协议及处理_socket_04            this._position += num;
 61Socket开发之通讯协议及处理_socket_04            return buffer;
 62Socket开发之通讯协议及处理_socket_13        }

 63Socket开发之通讯协议及处理_socket_04
 64Socket开发之通讯协议及处理_socket_04        public bool Read(out Message message)
 65Socket开发之通讯协议及处理_socket_10        {
 66Socket开发之通讯协议及处理_socket_04            message = null;
 67Socket开发之通讯协议及处理_socket_04            _position = 0;
 68Socket开发之通讯协议及处理_socket_04            if (_length > 6)
 69Socket开发之通讯协议及处理_socket_10            {
 70Socket开发之通讯协议及处理_socket_04                message = new Message();
 71Socket开发之通讯协议及处理_socket_04                message.Class = ReadByte();
 72Socket开发之通讯协议及处理_socket_04                message.Flag = ReadByte();
 73Socket开发之通讯协议及处理_socket_04                message.Size = ReadInt();
 74Socket开发之通讯协议及处理_socket_04                if (message.Size <= 0 || message.Size <= _length - _position)
 75Socket开发之通讯协议及处理_socket_10                {
 76Socket开发之通讯协议及处理_socket_04                    if (message.Size > 0)
 77Socket开发之通讯协议及处理_socket_10                    {
 78Socket开发之通讯协议及处理_socket_04                        message.Content = ReadBytes(message.Size);
 79Socket开发之通讯协议及处理_socket_13                    }

 80Socket开发之通讯协议及处理_socket_04                    Remove(message.Size + 6);
 81Socket开发之通讯协议及处理_socket_04                    return true;
 82Socket开发之通讯协议及处理_socket_13                }

 83Socket开发之通讯协议及处理_socket_04                else
 84Socket开发之通讯协议及处理_socket_10                {
 85Socket开发之通讯协议及处理_socket_04                    message = null;
 86Socket开发之通讯协议及处理_socket_04                    return false;
 87Socket开发之通讯协议及处理_socket_13                }

 88Socket开发之通讯协议及处理_socket_13            }

 89Socket开发之通讯协议及处理_socket_04            else
 90Socket开发之通讯协议及处理_socket_10            {
 91Socket开发之通讯协议及处理_socket_04                return false;
 92Socket开发之通讯协议及处理_socket_13            }

 93Socket开发之通讯协议及处理_socket_13        }

 94Socket开发之通讯协议及处理_socket_04
 95Socket开发之通讯协议及处理_socket_04        private void EnsureCapacity(int value)
 96Socket开发之通讯协议及处理_socket_10        {
 97Socket开发之通讯协议及处理_socket_04            if (value <= this._capacity)
 98Socket开发之通讯协议及处理_socket_04                return;
 99Socket开发之通讯协议及处理_socket_04            int num1 = value;
100Socket开发之通讯协议及处理_socket_04            if (num1 < 0x100)
101Socket开发之通讯协议及处理_socket_04                num1 = 0x100;
102Socket开发之通讯协议及处理_socket_04            if (num1 < (this._capacity * 2))
103Socket开发之通讯协议及处理_socket_04                num1 = this._capacity * 2;
104Socket开发之通讯协议及处理_socket_04            byte[] buffer1 = new byte[num1];
105Socket开发之通讯协议及处理_socket_04            if (this._length > 0)
106Socket开发之通讯协议及处理_socket_04                Buffer.BlockCopy(this._buffer, 0, buffer1, 0this._length);
107Socket开发之通讯协议及处理_socket_04            this._buffer = buffer1;
108Socket开发之通讯协议及处理_socket_04            this._capacity = num1;
109Socket开发之通讯协议及处理_socket_13        }

110Socket开发之通讯协议及处理_socket_04
111Socket开发之通讯协议及处理_socket_04        public void Write(byte[] buffer, int offset, int count)
112Socket开发之通讯协议及处理_socket_10        {
113Socket开发之通讯协议及处理_socket_04            if (buffer.Length - offset < count)
114Socket开发之通讯协议及处理_socket_10            {
115Socket开发之通讯协议及处理_socket_04                count = buffer.Length - offset;
116Socket开发之通讯协议及处理_socket_13            }

117Socket开发之通讯协议及处理_socket_04            EnsureCapacity(buffer.Length + count);
118Socket开发之通讯协议及处理_socket_04            Array.Clear(_buffer, _length, _capacity - _length);
119Socket开发之通讯协议及处理_socket_04            Buffer.BlockCopy(buffer, offset, _buffer, _length, count);
120Socket开发之通讯协议及处理_socket_04            _length += count;
121Socket开发之通讯协议及处理_socket_13        }

122Socket开发之通讯协议及处理_socket_04
123Socket开发之通讯协议及处理_socket_04        private void Remove(int count)
124Socket开发之通讯协议及处理_socket_10        {
125Socket开发之通讯协议及处理_socket_04            if (_length >= count)
126Socket开发之通讯协议及处理_socket_10            {
127Socket开发之通讯协议及处理_socket_04                Buffer.BlockCopy(_buffer, count, _buffer, 0, _length - count);
128Socket开发之通讯协议及处理_socket_04                _length -= count;
129Socket开发之通讯协议及处理_socket_04                Array.Clear(_buffer, _length, _capacity - _length);
130Socket开发之通讯协议及处理_socket_13            }

131Socket开发之通讯协议及处理_socket_04            else
132Socket开发之通讯协议及处理_socket_10            {
133Socket开发之通讯协议及处理_socket_04                _length = 0;
134Socket开发之通讯协议及处理_socket_04                Array.Clear(_buffer, 0, _capacity);
135Socket开发之通讯协议及处理_socket_13            }

136Socket开发之通讯协议及处理_socket_13        }

137Socket开发之通讯协议及处理_buffer_83    }

这个类的使用非常简单,你只要用Write(byte[] buffer, int offset, int count)将接收到的数据写入数据流中,并用bool Read(out Message message)将数据中的第一个数据包取出,如果函数返回True,就说明取回一个封包成功,如果返回False,则说明流中已经没有完整的封包,你需要继续接收后面的数据以组成一个完整的封包。

这们我们的数据分析就会变得非常简单。我们可以在ReceiveCallBack回调函数中将接收到的数据写入到流中并通知线程池中的工作者线程分析数据流并处理数据。我在前面的关于Socket异步操作的文章中的Analyzer函数就是用这两个类来分析处理数据的。这样的好处理就是,Socket工作线程只需要负责数据的接收,并将其写入流,其它的事情由其它的线程这处理,就不会因为处理的时间过长而导致接收操作被阻塞。从而影响Socket的性能。

本文所述方法只是协议处理的多种方法中的其中一种,而且可能并不是很优秀的方法,如果谁有更好的方法,还希望您能和我多多交流。好了,今天就到这里了,关于 Socket 的文章到这里可能就告一段落了,我现在在研究 VS2008 里面的新东西,如果有什么必得的话,我会继续写出来的。谢谢大家的支持。