TCP/IP網絡編程之數據包協議


一、概要

       在了解了網絡字節序之后,接下來就是要講最最重點的消息協議。數據包是什么呢,數據包可以理解為兩個人講電話說的每一句話的內容。通過大家約定好的方式去理解。達到只有接聽電話兩個人才懂的東西。在程序中如何體現出來呢,那么接着往下看。

技術交流QQ群:580749909  歡迎交流有問必答,文章尾有個人的微信公眾號有興趣的小伙伴多多關注。

二、簡介

       

 

 消息數據包主要是以二進制數組的形式存在,主要分為4個部分。

校驗位:校驗是否是雙方約定好的“暗號”,如果校驗位都不通過就沒必要再繼續往下解析節約處理時間。

消息頭:是描述整個消息長度以及其它附加信息。

消息體:描述消息的具體內容,比如說想獲取“股票歷史行情數據”這些具體想得到的內容序列化之后存放在消息體中。

消息尾:通常用來描述整個消息的結束,主要還是需要看應用場景消息尾可以不需要。

三、主要內容

代碼設計層面來說大致是基於以上的路線來走,首先我們腦子里有一個概念

1.消息是一個整體,這個整體分為四個部分(校驗位、消息頭、消息體、消息尾)

2.實際傳輸時以byte數組(二進制)的形式進行傳遞,將要寫入的內容轉換為字節按照順序依次寫入數組。解析同樣按照這個規則解析再轉為具體內容。

3.數據格式,上一條提到了二進制形式傳遞那怎么把你要傳的數據以一種標准的方式發送出去呢。這里舉例幾種數據格式json 還有 protobuf。推薦用protobuf序列化速度快數據包壓縮的體積更小。

4.大端小端的約定,服務方和客戶方約好我們傳輸寫入字節序的大小端。推薦統一用大端。

5.代碼設計

6.數據包的讀取順序,是從校驗位依次往后讀取至消息尾。

 

四、代碼設計

 

第一步,聲明一個接口來定義數據包基礎結構。

 public interface IPacket
    {
        byte[] Serialize();

        void Deserialize(ref byte[] data);
    }

 

第二步,分別定義響應(Respone)和請求(Request)數據包結構

 

響應(Respone)

 public class RpcRespone<TMessage> : IPacket
        where TMessage : IMessage
    {
        /// <summary>
        /// 4個byte表示package長度
        /// </summary>
        public int Length { get; private set; }

        /// <summary>
        /// package頭18個字節
        /// </summary>
        public RespHeader Header { get; set; }

        /// <summary>
        /// package內容
        /// </summary>
        public TMessage Body { get; set; }

        public byte[] Serialize()
        {
            var header = Header.Serialize();
            Length += header.Length;

            byte[] body = null;
            var json = JsonConvert.SerializeObject(Body);
            if (json != null)
            {
                body = Encoding.UTF8.GetBytes(json);
                Length += body.Length;
            }

            var package = new byte[4+Length];
            BytesWriter.Write(Length, ref package, 0);  //length
            BytesWriter.Write(header, ref package, 4);  //header
            if (body != null)
            {
                BytesWriter.Write(body, ref package, 4 + RespHeader.Length);   //body
            }
            
            return package;
        }

        public void Deserialize(ref byte[] data)
        {
            try
            {
                Length = BytesReader.ReadInt32(ref data, 0);

                var header = BytesReader.ReadBuffer(ref data, 4, RespHeader.Length);
                Header = new RespHeader();
                Header.Deserialize(ref header);
                var body = BytesReader.ReadBuffer(ref data, 4 + RespHeader.Length, Length - RespHeader.Length);
                var json = Encoding.UTF8.GetString(body);
                Body = JsonConvert.DeserializeObject<TMessage>(json);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("JsonConvert Deserialize. {0}", ex.Message);
            }
        }
}

 

    public class ReqHeader : IPacket
    {
        public const int Length = 24;

        /// <summary>
        /// 包頭標志,用於校驗
        /// </summary>
        public byte Checkbit { get; set; }

        /// <summary>
        /// 8個byte表示uid
        /// </summary>
        public long Uid { get; set; }

        /// <summary>
        /// 8個byte表示是requestId
        /// </summary>
        public long RequestId { get; set; }

        /// <summary>
        /// 1個bit表示是否加密
        /// </summary>
        public bool IsEncrypt { get; set; }

        /// <summary>
        /// 2個byte表示commandId
        /// </summary>
        public short CommandId { get; set; }

        /// <summary>
        /// 2個byte表示擴展位1的長度
        /// </summary>
        public short Ext1 { get; set; }

        /// <summary>
        /// 2個byte表示擴展位2的長度
        /// </summary>
        public short Ext2 { get; set; }

        public byte[] Serialize()
        {
            Checkbit = Header.Checkbit;
            var header = new byte[24];

            try
            {
                BytesWriter.Write(Checkbit, ref header, 0);
                BytesWriter.Write(Uid, ref header, 1);
                BytesWriter.Write(RequestId, ref header, 9);
                BytesWriter.Write(IsEncrypt, ref header, 17);
                BytesWriter.Write(CommandId, ref header, 18);
                BytesWriter.Write(Ext1, ref header, 20);
                BytesWriter.Write(Ext2, ref header, 22);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("Serialize Request Header Exception. {0}", ex.Message);
            }
          
            return header;
        }

        public void Deserialize(ref byte[] data)
        {
            try
            {
                Checkbit = BytesReader.ReadByte(ref data, 0);
                Uid = BytesReader.ReadInt64(ref data, 1);
                RequestId = BytesReader.ReadInt64(ref data, 9);
                IsEncrypt = BytesReader.ReadBool(ref data, 17);
                CommandId = BytesReader.ReadInt16(ref data, 18);
                Ext1 = BytesReader.ReadInt16(ref data, 20);
                Ext2 = BytesReader.ReadInt16(ref data, 22);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("Serialize Request Header Exception. {0}", ex.Message);
            }
        }
     } 

 

 

請求(Request)

  public class RpcRequest<TMessage> : IPacket
        where TMessage : class, IMessage
    {
        /// <summary>
        /// 4個byte表示package長度
        /// </summary>
        public int Length { get; private set; }

        /// <summary>
        /// package頭24個字節
        /// </summary>
        public ReqHeader Header{ get; set; }

        /// <summary>
        /// package內容
        /// </summary>
        public TMessage Body { get; set; }

        public byte[] Serialize()
        {
            var header = Header.Serialize();
            Length += header.Length;

            byte[] body = null;
            var json = JsonConvert.SerializeObject(Body);
            if (json != null)
            {
                body = Encoding.UTF8.GetBytes(json);
                Length += body.Length;
            }

            var package = new byte[4 + Length];
            BytesWriter.Write(Length, ref package, 0);  //length
            BytesWriter.Write(header, ref package, 4);  //header
            if (body != null)
            {
                BytesWriter.Write(body, ref package, 4 + ReqHeader.Length);   //body
            }

            return package;
        }

        public void Deserialize(ref byte[] data)
        {
            try
            {
                Length = BytesReader.ReadInt32(ref data, 0);

                var header = BytesReader.ReadBuffer(ref data, 4, ReqHeader.Length);
                Header = new ReqHeader();
                Header.Deserialize(ref header);

                var body = BytesReader.ReadBuffer(ref data, 4 + ReqHeader.Length, Length - ReqHeader.Length);
                var json = Encoding.UTF8.GetString(body);
                //LogHepler.Log.DebugFormat("JsonConvert Deserialize. Id:[{0}] body:[{1}]", Header.RequestId, json);
                Body = JsonConvert.DeserializeObject<TMessage>(json);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("JsonConvert Deserialize. {0}", ex.Message);
            }
        }
    }

 

 public class RespHeader : IPacket
    {
        public const int Length = 18;

        /// <summary>
        /// 包頭標志,用於校驗
        /// </summary>
        public byte Checkbit { get; set; }

        /// <summary>
        /// 8個字節,請求ID
        /// </summary>
        public long RequestId { get; set; }

        /// <summary>
        /// 4個字節,返回結果狀態碼
        /// </summary>
        public int Code { get; set; }

        /// <summary>
        /// 1個字節,是否加密
        /// </summary>
        public bool IsEncrypt { get; set; }

        /// <summary>
        /// 4個字節,命令ID
        /// </summary>
        public short CommandId { get;set; }

        /// <summary>
        /// 2個字節,擴展參數
        /// </summary>
        public short Ext1{get; set; }


        public byte[] Serialize()
        {
            Checkbit = Header.Checkbit;
            var header = new byte[18];
            try
            {
                BytesWriter.Write(Checkbit, ref header, 0);
                BytesWriter.Write(RequestId, ref header, 1);
                BytesWriter.Write(Code, ref header, 9);
                BytesWriter.Write(IsEncrypt, ref header, 13);
                BytesWriter.Write(CommandId, ref header, 14);
                BytesWriter.Write(Ext1, ref header, 16);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("Serialize Respone Header Exception. {0}", ex.Message);
            }
       
            return header;
        }

        public void Deserialize(ref byte[] data)
        {
            try
            {
                Checkbit = BytesReader.ReadByte(ref data, 0);
                RequestId = BytesReader.ReadInt64(ref data, 1);
                Code = BytesReader.ReadInt32(ref data, 9);
                IsEncrypt = BytesReader.ReadBool(ref data, 13);
                CommandId = BytesReader.ReadInt16(ref data, 14);
                Ext1 = BytesReader.ReadInt16(ref data, 16);
            }
            catch (Exception ex)
            {
                LogHepler.Log.ErrorFormat("Deserialize Respone Header Exception. {0}", ex.Message);
            }
        }
    }

以上基本是數據包的設計思路和代碼設計的實現。后面會專門寫博客專門講解實戰應用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM