天天看點

Socket開發之通訊協定及處理

在Socket應用開發中,還有一個話題是讨論的比較多的,那就是資料接收後如何處理的問題。這也是一個令剛接觸Socket開發的人很頭疼的問題。

因為Socket的TCP通訊中有一個“粘包”的現象,既:大多數時候發送端多次發送的小資料包會被連在一起被接收端同時接收到,多個小包被組成一個大包被接收。有時候一個大資料包又會被拆成多個小資料包發送。這樣就存在一個将資料包拆分和重新組合的問題。那麼如何去處理這個問題呢?這就是我今天要講的通訊協定。

所謂的協定就是通訊雙方協商并制定好要傳送的資料的結構與格式。并按制定好的格式去組合與分析資料。進而使資料得以被準确的了解和處理。

那麼我們如何去制定通訊協定呢?很簡單,就是指定資料中各個位元組所代表的意義。比如說:第一位代表封標頭,第二位代表封類型,第三、四位代表封包的資料長度。然後後面是實際的資料内容。

如下面這個例子:

<col>

01

06 00

01 0f ef 87 56 34

協定類别

協定代碼

資料長度

實際資料

前面三部分稱之為封標頭,它的長度是固定的,第四部分是封包資料,它的長度是不固定的,由第三部分辨別其長度。因為我們的協定将用在TCP中,是以我沒有加入校驗位。原因是TCP可以保證資料的完整性。校驗位是沒有必要存在的。

接下來我們要為這個資料封包聲明一個類來封裝它:

Socket開發之通訊協定及處理

 1

Socket開發之通訊協定及處理

    public class Message

 2

Socket開發之通訊協定及處理

    {

 3

Socket開發之通訊協定及處理

        private byte _class;

 4

Socket開發之通訊協定及處理

        private byte _flag;

 5

Socket開發之通訊協定及處理

        private int _size;

 6

Socket開發之通訊協定及處理

        private byte[] _content;

 7

Socket開發之通訊協定及處理

 8

Socket開發之通訊協定及處理

        public byte[] Content

 9

Socket開發之通訊協定及處理

        {

10

Socket開發之通訊協定及處理

            get { return _content; }

11

Socket開發之通訊協定及處理

            set { _content = value; }

12

Socket開發之通訊協定及處理

        }

13

Socket開發之通訊協定及處理

14

Socket開發之通訊協定及處理

        public int Size

15

Socket開發之通訊協定及處理

16

Socket開發之通訊協定及處理

            get { return _size; }

17

Socket開發之通訊協定及處理

            set { _size = value; }

18

Socket開發之通訊協定及處理

19

Socket開發之通訊協定及處理

20

Socket開發之通訊協定及處理

        public byte Flag

21

Socket開發之通訊協定及處理

22

Socket開發之通訊協定及處理

            get { return _flag; }

23

Socket開發之通訊協定及處理

            set { _flag = value; }

24

Socket開發之通訊協定及處理

25

Socket開發之通訊協定及處理

26

Socket開發之通訊協定及處理

        public byte Class

27

Socket開發之通訊協定及處理

28

Socket開發之通訊協定及處理

            get { return _class; }

29

Socket開發之通訊協定及處理

            set { _class = value; }

30

Socket開發之通訊協定及處理

31

Socket開發之通訊協定及處理

32

Socket開發之通訊協定及處理

        public Message()

33

Socket開發之通訊協定及處理

34

Socket開發之通訊協定及處理

35

Socket開發之通訊協定及處理

36

Socket開發之通訊協定及處理

37

Socket開發之通訊協定及處理

        public Message(byte @class, byte flag, byte[] content)

38

Socket開發之通訊協定及處理

39

Socket開發之通訊協定及處理

            _class = @class;

40

Socket開發之通訊協定及處理

            _flag = flag;

41

Socket開發之通訊協定及處理

            _size = content.Length;

42

Socket開發之通訊協定及處理

            _content = content;

43

Socket開發之通訊協定及處理

44

Socket開發之通訊協定及處理

45

Socket開發之通訊協定及處理

        public byte[] ToBytes()

46

Socket開發之通訊協定及處理

47

Socket開發之通訊協定及處理

            byte[] _byte;

48

Socket開發之通訊協定及處理

            using (MemoryStream mem = new MemoryStream())

49

Socket開發之通訊協定及處理

            {

50

Socket開發之通訊協定及處理

                BinaryWriter writer = new BinaryWriter(mem);

51

Socket開發之通訊協定及處理

                writer.Write(_class);

52

Socket開發之通訊協定及處理

                writer.Write(_flag);

53

Socket開發之通訊協定及處理

                writer.Write(_size);

54

Socket開發之通訊協定及處理

                if (_size &gt; 0)

55

Socket開發之通訊協定及處理

                {

56

Socket開發之通訊協定及處理

                    writer.Write(_content);

57

Socket開發之通訊協定及處理

                }

58

Socket開發之通訊協定及處理

                _byte = mem.ToArray();

59

Socket開發之通訊協定及處理

                writer.Close();

60

Socket開發之通訊協定及處理

            }

61

Socket開發之通訊協定及處理

            return _byte;

62

Socket開發之通訊協定及處理

63

Socket開發之通訊協定及處理

64

Socket開發之通訊協定及處理

        public static Message FromBytes(byte[] Buffer)

65

Socket開發之通訊協定及處理

66

Socket開發之通訊協定及處理

            Message message = new Message();

67

Socket開發之通訊協定及處理

            using (MemoryStream mem = new MemoryStream(Buffer))

68

Socket開發之通訊協定及處理

69

Socket開發之通訊協定及處理

                BinaryReader reader = new BinaryReader(mem);

70

Socket開發之通訊協定及處理

                message._class = reader.ReadByte();

71

Socket開發之通訊協定及處理

                message._flag = reader.ReadByte();

72

Socket開發之通訊協定及處理

                message._size = reader.ReadInt32();

73

Socket開發之通訊協定及處理

                if (message._size &gt; 0)

74

Socket開發之通訊協定及處理

75

Socket開發之通訊協定及處理

                    message._content = reader.ReadBytes(message._size);

76

Socket開發之通訊協定及處理

77

Socket開發之通訊協定及處理

                reader.Close();

78

Socket開發之通訊協定及處理

79

Socket開發之通訊協定及處理

            return message;

80

Socket開發之通訊協定及處理

81

Socket開發之通訊協定及處理

82

Socket開發之通訊協定及處理

    }

我們可以用Tobytes()和FromBytes()将封包轉換成二進制數組和從二進制數組轉換回來。

事情看起來已經解決了,但……真的是這樣子嗎?不然,我們知道,TCP資料是以流的形式被傳送的,我們并不知道一個資料包是否被傳送完畢,也不知道我們接收回來的資料包中是否有多個資料包,如果直接使用FromBytes()來轉換的話,很可能會因為資料不完整而出現異常,也有可能會因為資料中含有多個資料包而導緻資料丢失(因為你并不知道這些資料中含有多少個資料包)。那我們該怎麼辦?這也不難,我們先把接收回來的資料寫入一個流中。然後分析其中是否有完整的資料包,如果有,将其從流中取出,并将這部分資料從流中清除。直到流中沒有完整的資料為止,以後接收回來的資料就将其寫入流的結尾處,并從頭繼續分析。直到結束。

讓我們來看看這部分的代碼:

Socket開發之通訊協定及處理

  1

Socket開發之通訊協定及處理

    public class MessageStream

  2

Socket開發之通訊協定及處理

  3

Socket開發之通訊協定及處理

        private byte[] _buffer;

  4

Socket開發之通訊協定及處理

        private int _position;

  5

Socket開發之通訊協定及處理

        private int _length;

  6

Socket開發之通訊協定及處理

        private int _capacity;

  7

Socket開發之通訊協定及處理

  8

Socket開發之通訊協定及處理

        public MessageStream()

  9

Socket開發之通訊協定及處理

 10

Socket開發之通訊協定及處理

            _buffer = new byte[0];

 11

Socket開發之通訊協定及處理

            _position = 0;

 12

Socket開發之通訊協定及處理

            _length = 0;

 13

Socket開發之通訊協定及處理

            _capacity = 0;

 14

Socket開發之通訊協定及處理

 15

Socket開發之通訊協定及處理

 16

Socket開發之通訊協定及處理

        private byte ReadByte()

 17

Socket開發之通訊協定及處理

 18

Socket開發之通訊協定及處理

            if (this._position &gt;= this._length)

 19

Socket開發之通訊協定及處理

 20

Socket開發之通訊協定及處理

                return 0;

 21

Socket開發之通訊協定及處理

 22

Socket開發之通訊協定及處理

            return this._buffer[this._position++];

 23

Socket開發之通訊協定及處理

 24

Socket開發之通訊協定及處理

 25

Socket開發之通訊協定及處理

        private int ReadInt()

 26

Socket開發之通訊協定及處理

 27

Socket開發之通訊協定及處理

            int num = this._position += 4;

 28

Socket開發之通訊協定及處理

            if (num &gt; this._length)

 29

Socket開發之通訊協定及處理

 30

Socket開發之通訊協定及處理

                this._position = this._length;

 31

Socket開發之通訊協定及處理

                return -1;

 32

Socket開發之通訊協定及處理

 33

Socket開發之通訊協定及處理

            return (((this._buffer[num - 4] | (this._buffer[num - 3] &lt;&lt; 8)) | (this._buffer[num - 2] &lt;&lt; 0x10)) | (this._buffer[num - 1] &lt;&lt; 0x18));

 34

Socket開發之通訊協定及處理

 35

Socket開發之通訊協定及處理

 36

Socket開發之通訊協定及處理

        private byte[] ReadBytes(int count)

 37

Socket開發之通訊協定及處理

 38

Socket開發之通訊協定及處理

            int num = this._length - this._position;

 39

Socket開發之通訊協定及處理

            if (num &gt; count)

 40

Socket開發之通訊協定及處理

 41

Socket開發之通訊協定及處理

                num = count;

 42

Socket開發之通訊協定及處理

 43

Socket開發之通訊協定及處理

            if (num &lt;= 0)

 44

Socket開發之通訊協定及處理

 45

Socket開發之通訊協定及處理

                return null;

 46

Socket開發之通訊協定及處理

 47

Socket開發之通訊協定及處理

            byte[] buffer = new byte[num];

 48

Socket開發之通訊協定及處理

            if (num &lt;= 8)

 49

Socket開發之通訊協定及處理

 50

Socket開發之通訊協定及處理

                int num2 = num;

 51

Socket開發之通訊協定及處理

                while (--num2 &gt;= 0)

 52

Socket開發之通訊協定及處理

 53

Socket開發之通訊協定及處理

                    buffer[num2] = this._buffer[this._position + num2];

 54

Socket開發之通訊協定及處理

 55

Socket開發之通訊協定及處理

 56

Socket開發之通訊協定及處理

            else

 57

Socket開發之通訊協定及處理

 58

Socket開發之通訊協定及處理

                Buffer.BlockCopy(this._buffer, this._position, buffer, 0, num);

 59

Socket開發之通訊協定及處理

 60

Socket開發之通訊協定及處理

            this._position += num;

 61

Socket開發之通訊協定及處理

            return buffer;

 62

Socket開發之通訊協定及處理

 63

Socket開發之通訊協定及處理

 64

Socket開發之通訊協定及處理

        public bool Read(out Message message)

 65

Socket開發之通訊協定及處理

 66

Socket開發之通訊協定及處理

            message = null;

 67

Socket開發之通訊協定及處理

 68

Socket開發之通訊協定及處理

            if (_length &gt; 6)

 69

Socket開發之通訊協定及處理

 70

Socket開發之通訊協定及處理

                message = new Message();

 71

Socket開發之通訊協定及處理

                message.Class = ReadByte();

 72

Socket開發之通訊協定及處理

                message.Flag = ReadByte();

 73

Socket開發之通訊協定及處理

                message.Size = ReadInt();

 74

Socket開發之通訊協定及處理

                if (message.Size &lt;= 0 || message.Size &lt;= _length - _position)

 75

Socket開發之通訊協定及處理

 76

Socket開發之通訊協定及處理

                    if (message.Size &gt; 0)

 77

Socket開發之通訊協定及處理

                    {

 78

Socket開發之通訊協定及處理

                        message.Content = ReadBytes(message.Size);

 79

Socket開發之通訊協定及處理

                    }

 80

Socket開發之通訊協定及處理

                    Remove(message.Size + 6);

 81

Socket開發之通訊協定及處理

                    return true;

 82

Socket開發之通訊協定及處理

 83

Socket開發之通訊協定及處理

                else

 84

Socket開發之通訊協定及處理

 85

Socket開發之通訊協定及處理

                    message = null;

 86

Socket開發之通訊協定及處理

                    return false;

 87

Socket開發之通訊協定及處理

 88

Socket開發之通訊協定及處理

 89

Socket開發之通訊協定及處理

 90

Socket開發之通訊協定及處理

 91

Socket開發之通訊協定及處理

                return false;

 92

Socket開發之通訊協定及處理

 93

Socket開發之通訊協定及處理

 94

Socket開發之通訊協定及處理

 95

Socket開發之通訊協定及處理

        private void EnsureCapacity(int value)

 96

Socket開發之通訊協定及處理

 97

Socket開發之通訊協定及處理

            if (value &lt;= this._capacity)

 98

Socket開發之通訊協定及處理

                return;

 99

Socket開發之通訊協定及處理

            int num1 = value;

100

Socket開發之通訊協定及處理

            if (num1 &lt; 0x100)

101

Socket開發之通訊協定及處理

                num1 = 0x100;

102

Socket開發之通訊協定及處理

            if (num1 &lt; (this._capacity * 2))

103

Socket開發之通訊協定及處理

                num1 = this._capacity * 2;

104

Socket開發之通訊協定及處理

            byte[] buffer1 = new byte[num1];

105

Socket開發之通訊協定及處理

            if (this._length &gt; 0)

106

Socket開發之通訊協定及處理

                Buffer.BlockCopy(this._buffer, 0, buffer1, 0, this._length);

107

Socket開發之通訊協定及處理

            this._buffer = buffer1;

108

Socket開發之通訊協定及處理

            this._capacity = num1;

109

Socket開發之通訊協定及處理

110

Socket開發之通訊協定及處理

111

Socket開發之通訊協定及處理

        public void Write(byte[] buffer, int offset, int count)

112

Socket開發之通訊協定及處理

113

Socket開發之通訊協定及處理

            if (buffer.Length - offset &lt; count)

114

Socket開發之通訊協定及處理

115

Socket開發之通訊協定及處理

                count = buffer.Length - offset;

116

Socket開發之通訊協定及處理

117

Socket開發之通訊協定及處理

            EnsureCapacity(buffer.Length + count);

118

Socket開發之通訊協定及處理

            Array.Clear(_buffer, _length, _capacity - _length);

119

Socket開發之通訊協定及處理

            Buffer.BlockCopy(buffer, offset, _buffer, _length, count);

120

Socket開發之通訊協定及處理

            _length += count;

121

Socket開發之通訊協定及處理

122

Socket開發之通訊協定及處理

123

Socket開發之通訊協定及處理

        private void Remove(int count)

124

Socket開發之通訊協定及處理

125

Socket開發之通訊協定及處理

            if (_length &gt;= count)

126

Socket開發之通訊協定及處理

127

Socket開發之通訊協定及處理

                Buffer.BlockCopy(_buffer, count, _buffer, 0, _length - count);

128

Socket開發之通訊協定及處理

                _length -= count;

129

Socket開發之通訊協定及處理

                Array.Clear(_buffer, _length, _capacity - _length);

130

Socket開發之通訊協定及處理

131

Socket開發之通訊協定及處理

132

Socket開發之通訊協定及處理

133

Socket開發之通訊協定及處理

                _length = 0;

134

Socket開發之通訊協定及處理

                Array.Clear(_buffer, 0, _capacity);

135

Socket開發之通訊協定及處理

136

Socket開發之通訊協定及處理

137

Socket開發之通訊協定及處理

這個類的使用非常簡單,你隻要用Write(byte[] buffer,int offset, int count)将接收到的資料寫入資料流中,并用bool

Read(out Message message)将資料中的第一個資料包取出,如果函數傳回True,就說明取回一個封包成功,如果傳回False,則說明流中已經沒有完整的封包,你需要繼續接收後面的資料以組成一個完整的封包。

這們我們的資料分析就會變得非常簡單。我們可以在ReceiveCallBack回調函數中将接收到的資料寫入到流中并通知線程池中的工作者線程分析資料流并處理資料。我在前面的關于Socket異步操作的文章中的Analyzer函數就是用這兩個類來分析處理資料的。這樣的好處理就是,Socket工作線程隻需要負責資料的接收,并将其寫入流,其它的事情由其它的線程這處理,就不會因為處理的時間過長而導緻接收操作被阻塞。進而影響Socket的性能。

本文所述方法隻是協定處理的多種方法中的其中一種,而且可能并不是很優秀的方法,如果誰有更好的方法,還希望您能和我多多交流。好了,今天就到這裡了,關于Socket的文章到這裡可能就告一段落了,我現在在研究VS2008裡面的新東西,如果有什麼必得的話,我會繼續寫出來的。謝謝大家的支援。