TCP通信之数据流粘包和拆包解决方法
产生数据粘包和拆包的原因这里不再赘述。本文简单介绍三种解决方法。
1. 定长协议
这种方式发送方发送的消息都具有固定的长度,比如每个消息长度都是1024个字节。
如果发送的消息内容没有1024个字节长度,那么消息尾部补0填充直到长度达到1024个字节,发送的消息内容不能超过1024个字节。
接收方收到消息后,每1024个字节截取为一段,认为是一个完整的消息。
缺点:发送的消息不能大于固定长度;消息尾部补充的0不易清除;浪费带宽(如果消息只有24个字节,那么每次需要发1024个字节数据,浪费1000个字节)

2.特殊字符分隔协议
这种方法使用一个特殊的字符,比如'\r'。
发送方发送消息的尾部加上这个字符用来和下一个消息区分开。
接收方每收到消息时,首先将这一震消息添加到缓存队列中,然后取出缓存中全部消息,判断消息中是否包含特殊字符,如果没有说明还没有收到一个完整的消息,继续等待,如果消息中包含特殊字符,则截取出特殊字符前面的所有内容作为一个完整的消息。
缺点:消息体中不能出现特殊字符

编码过程:
public class DelimiterBasedFrameEncoder : IEncoder { const char DELIMITER = '\r'; public byte[] Encoder(byte[] msg) { byte[] tail = BitConverter.GetBytes(DELIMITER); List<byte> sendMsg = new List<byte>(); sendMsg.AddRange(msg); sendMsg.AddRange(tail); return sendMsg.ToArray(); } }
解码过程:
public class DelimiterBasedFrameDecoder : IDecoder { const char DELIMITER = '\r'; public byte[] Decoder(List<byte> msg) { byte[] tail = BitConverter.GetBytes(DELIMITER); int index = FindIndexDelimiter(msg.ToArray(), tail); if (index == -1) { return null; } byte[] result = new byte[index]; Array.Copy(msg.ToArray(), result, index); msg.RemoveRange(0, index + tail.Length ); return result; } int FindIndexDelimiter(byte[] maxSource, byte[] smallSource) { int index = maxSource.ToList().IndexOf(smallSource.First()); for (int i = 1; i < smallSource.Length; i++) { int id= maxSource.ToList().IndexOf(smallSource[i]); if (id - index != i) { return -1; } } return index; } }
3.变长协议
将消息分为两个部分,前面一部分叫做消息头,长度4个字节,后面一部分叫做消息体,长度为发送消息实际长度。
发送方发送消息时,首先获得消息的长度数值L,然后将L转换为4个字节的数组,将这个数组加到消息体前,形成一个新的字节数组发送。
接收方收到消息首先添加到缓存队列中,然后取出缓存中全部消息,判断消息的字节数组长度是否大4,如果小于则不做处理,等待后续的消息;如果大于4则取出前4个字节,将其转换为整型数字叫做L,然后再判断整个消息长度减去4(消息头)是否大于L,
如果不大于则不做处理,继续等待后续消息;如果大于L,则从第5个字节开始取出L个字节作为一个完整的消息,然后再将缓存中前4+L个字节删除。
假如发送队列中有3个数据待发送,到达接收缓存队列中可能是下图显示的样子。

发送方构造发送内容:

接收方解析数据:

编码过程:
public class LengthFieldBasedFrameEncoder : IEncoder { public byte[] Encoder(byte[] msg) { byte[] header = BitConverter.GetBytes(msg.Length); List<byte> sendMsg = new List<byte>(); sendMsg.AddRange(header); sendMsg.AddRange(msg); return sendMsg.ToArray(); } }
解码过程:
public class LengthFieldBasedFrameDecoder : IDecoder { public byte[] Decoder(List<byte> msgCache) { byte[] result = null; int cacheLength = msgCache.Count; if (cacheLength < 4) { return result; } int bodyLength = BitConverter.ToInt32(msgCache.ToArray(), 0); cacheLength = msgCache.Count; if (cacheLength - 4 < bodyLength) { return result; } result = new byte[bodyLength]; result = msgCache.Skip(4).Take(bodyLength).ToArray(); msgCache.RemoveRange(0, 4 + bodyLength); return result; } }

浙公网安备 33010602011771号