TCP/IP网络编程之数据包协议

Posted justzhuzhu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP/IP网络编程之数据包协议相关的知识,希望对你有一定的参考价值。

一、概要

       在了解了网络字节序之后,接下来就是要讲最最重点的消息协议。数据包是什么呢,数据包可以理解为两个人讲电话说的每一句话的内容。通过大家约定好的方式去理解。达到只有接听电话两个人才懂的东西。在程序中如何体现出来呢,那么接着往下看。

技术交流QQ群:580749909  欢迎交流有问必答,文章尾有个人的微信公众号有兴趣的小伙伴多多关注。

二、简介

       技术图片

 

 消息数据包主要是以二进制数组的形式存在,主要分为4个部分。

校验位:校验是否是双方约定好的“暗号”,如果校验位都不通过就没必要再继续往下解析节约处理时间。

消息头:是描述整个消息长度以及其它附加信息。

消息体:描述消息的具体内容,比如说想获取“股票历史行情数据”这些具体想得到的内容序列化之后存放在消息体中。

消息尾:通常用来描述整个消息的结束,主要还是需要看应用场景消息尾可以不需要。

三、主要内容

代码设计层面来说大致是基于以上的路线来走,首先我们脑子里有一个概念

1.消息是一个整体,这个整体分为四个部分(校验位、消息头、消息体、消息尾)

2.实际传输时以byte数组(二进制)的形式进行传递,将要写入的内容转换为字节按照顺序依次写入数组。解析同样按照这个规则解析再转为具体内容。

3.数据格式,上一条提到了二进制形式传递那怎么把你要传的数据以一种标准的方式发送出去呢。这里举例几种数据格式json 还有 protobuf。推荐用protobuf序列化速度快数据包压缩的体积更小。

4.大端小端的约定,服务方和客户方约好我们传输写入字节序的大小端。推荐统一用大端。

5.代码设计

 

四、代码设计

 

第一步,声明一个接口来定义数据包基础结构。

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

以上基本是数据包的设计思路和代码设计的实现。后面会专门写博客专门讲解实战应用。

技术图片

以上是关于TCP/IP网络编程之数据包协议的主要内容,如果未能解决你的问题,请参考以下文章

即时通讯开发之详解TCP/IP中的广播和多播IGMP协议

计算机网络—网络原理之TCP/IP协议

TCP/IP协议之IP层

TCP/IP协议 网络层

Linux之网络基础?

tcp/ip协议能不能连接二个不同的网络?