Socket开发之通讯协议及处理
因为Socket的TCP通讯中有一个“粘包”的现象,既:大多数时候发送端多次发送的小数据包会被连在一起被接收端同时接收到,多个小包被组成一个大包被接收。有时候一个大数据包又会被拆成多个小数据包发送。这样就存在一个将数据包拆分和重新组合的问题。那么如何去处理这个问题呢?这就是我今天要讲的通讯协议。
所谓的协议就是通讯双方协商并制定好要传送的数据的结构与格式。并按制定好的格式去组合与分析数据。从而使数据得以被准确的理解和处理。
那么我们如何去制定通讯协议呢?很简单,就是指定数据中各个字节所代表的意义。比如说:第一位代表封包头,第二位代表封类型,第三、四位代表封包的数据长度。然后后面是实际的数据内容。
如下面这个例子:
| 01 | 01 | 06 00 | 01 0f ef 87 56 34 |
| 协议类别 | 协议代码 | 数据长度 | 实际数据 |
前面三部分称之为封包头,它的长度是固定的,第四部分是封包数据,它的长度是不固定的,由第三部分标识其长度。因为我们的协议将用在TCP中,所以我没有加入校验位。原因是TCP可以保证数据的完整性。校验位是没有必要存在的。
接下来我们要为这个数据封包声明一个类来封装它:
我们可以用Tobytes()和FromBytes()将封包转换成二进制数组和从二进制数组转换回来。
事情看起来已经解决了,但……真的是这样子吗?不然,我们知道,TCP数据是以流的形式被传送的,我们并不知道一个数据包是否被传送完毕,也不知道我们接收回来的数据包中是否有多个数据包,如果直接使用FromBytes()来转换的话,很可能会因为数据不完整而出现异常,也有可能会因为数据中含有多个数据包而导致数据丢失(因为你并不知道这些数据中含有多少个数据包)。那我们该怎么办?这也不难,我们先把接收回来的数据写入一个流中。然后分析其中是否有完整的数据包,如果有,将其从流中取出,并将这部分数据从流中清除。直到流中没有完整的数据为止,以后接收回来的数据就将其写入流的结尾处,并从头继续分析。直到结束。
让我们来看看这部分的代码:<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->1
publicclassMessageStream2


{3
privatebyte[]_buffer;4
privateint_position;5
privateint_length;6
privateint_capacity;7

8
publicMessageStream()9


{10
_buffer=newbyte[0];11
_position=0;12
_length=0;13
_capacity=0;14
}15

16
privatebyteReadByte()17


{18
if(this._position>=this._length)19


{20
return0;21
}22
returnthis._buffer[this._position++];23
}24

25
privateintReadInt()26


{27
intnum=this._position+=4;28
if(num>this._length)29


{30
this._position=this._length;31
return-1;32
}33
return(((this._buffer[num-4]|(this._buffer[num-3]<<8))|(this._buffer[num-2]<<0x10))|(this._buffer[num-1]<<0x18));34
}35

36
privatebyte[]ReadBytes(intcount)37


{38
intnum=this._length-this._position;39
if(num>count)40


{41
num=count;42
}43
if(num<=0)44


{45
returnnull;46
}47
byte[]buffer=newbyte[num];48
if(num<=8)49


{50
intnum2=num;51
while(--num2>=0)52


{53
buffer[num2]=this._buffer[this._position+num2];54
}55
}56
else57


{58
Buffer.BlockCopy(this._buffer,this._position,buffer,0,num);59
}60
this._position+=num;61
returnbuffer;62
}63

64
publicboolRead(outMessagemessage)65


{66
message=null;67
_position=0;68
if(_length>6)69


{70
message=newMessage();71
message.Class=ReadByte();72
message.Flag=ReadByte();73
message.Size=ReadInt();74
if(message.Size<=0||message.Size<=_length-_position)75


{76
if(message.Size>0)77


{78
message.Content=ReadBytes(message.Size);79
}80
Remove(message.Size+6);81
returntrue;82
}83
else84


{85
message=null;86
returnfalse;87
}88
}89
else90


{91
returnfalse;92
本文探讨了Socket开发中数据包处理的难点,特别是TCP通讯中的“粘包”问题。介绍了如何通过定义通讯协议来解决数据包的拆分与重组,包括制定封包结构、使用类进行封装,以及实现数据包的读取与解析。
2640

被折叠的 条评论
为什么被折叠?



