1. 程式人生 > 其它 >TCP通訊之資料流粘包和拆包解決方法

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個位元組刪除。

傳送方構造傳送內容:

接收方解析資料:

編碼過程:

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;
        }
    }