粘包
使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。粘包可能由发送方造成,也可能由接收方造成。TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据,造成多个数据包的粘连。如果接收进程不及时接收数据,已收到的数据就放在系统接收缓冲区,用户进程读取数据时就可能同时读到多个数据包。
粘包一般的解决办法是制定通讯协议,由协议来规定如何分包解包。
分包
在NETIOCPDemo例子程序中,我们分包的逻辑是先发一个长度,然后紧接着是数据包内容,这样就可以把每个包分开。
应用层数据包格式如下:
AsyncSocketInvokeElement分包处理主要代码,我们收到的数据都是在ProcessReceive方法中处理,处理的方法是把收到的数据存到缓冲区数组中,然后取前4个字节为长度,如果剩下的字节数大于等于长度,则取到一个完整包,进行后续逻辑处理,如果取到的不够一个包,则不处理,等待后续包接收,具体代码如下:
应用层数据包格式 数据包长度Len:Cardinal(4字节无符号整数) 数据包内容,长度为Len
[csharp] view plain copy
- public virtual bool ProcessReceive(byte[] buffer, int offset, int count) //接收异步事件返回的数据,用于对数据进行缓存和分包
- {
- m_activeDT = DateTime.UtcNow;
- DynamicBufferManager receiveBuffer = m_asyncSocketUserToken.ReceiveBuffer;
- receiveBuffer.WriteBuffer(buffer, offset, count);
- if (receiveBuffer.DataCount > sizeof(int))
- {
- //按照长度分包
- int packetLength = BitConverter.ToInt32(receiveBuffer.Buffer, 0); //获取包长度
- if (NetByteOrder)
- packetLength = System.Net.IPAddress.NetworkToHostOrder(packetLength); //把网络字节顺序转为本地字节顺序
- if ((packetLength > 10 * 1024 * 1024) | (receiveBuffer.DataCount > 10 * 1024 * 1024)) //最大Buffer异常保护
- return false;
- if ((receiveBuffer.DataCount - sizeof(int)) >= packetLength) //收到的数据达到包长度
- {
- bool result = ProcessPacket(receiveBuffer.Buffer, sizeof(int), packetLength);
- if (result)
- receiveBuffer.Clear(packetLength + sizeof(int)); //从缓存中清理
- return result;
- }
- else
- {
- return true;
- }
- }
- else
- {
- return true;
- }
- }