一、概要

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

技术交流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[+Length];
BytesWriter.Write(Length, ref package, ); //length
BytesWriter.Write(header, ref package, ); //header
if (body != null)
{
BytesWriter.Write(body, ref package, + RespHeader.Length); //body
} return package;
} public void Deserialize(ref byte[] data)
{
try
{
Length = BytesReader.ReadInt32(ref data, ); var header = BytesReader.ReadBuffer(ref data, , RespHeader.Length);
Header = new RespHeader();
Header.Deserialize(ref header);
var body = BytesReader.ReadBuffer(ref data, + 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 = ; /// <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[]; try
{
BytesWriter.Write(Checkbit, ref header, );
BytesWriter.Write(Uid, ref header, );
BytesWriter.Write(RequestId, ref header, );
BytesWriter.Write(IsEncrypt, ref header, );
BytesWriter.Write(CommandId, ref header, );
BytesWriter.Write(Ext1, ref header, );
BytesWriter.Write(Ext2, ref header, );
}
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, );
Uid = BytesReader.ReadInt64(ref data, );
RequestId = BytesReader.ReadInt64(ref data, );
IsEncrypt = BytesReader.ReadBool(ref data, );
CommandId = BytesReader.ReadInt16(ref data, );
Ext1 = BytesReader.ReadInt16(ref data, );
Ext2 = BytesReader.ReadInt16(ref data, );
}
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[ + Length];
BytesWriter.Write(Length, ref package, ); //length
BytesWriter.Write(header, ref package, ); //header
if (body != null)
{
BytesWriter.Write(body, ref package, + ReqHeader.Length); //body
} return package;
} public void Deserialize(ref byte[] data)
{
try
{
Length = BytesReader.ReadInt32(ref data, ); var header = BytesReader.ReadBuffer(ref data, , ReqHeader.Length);
Header = new ReqHeader();
Header.Deserialize(ref header); var body = BytesReader.ReadBuffer(ref data, + 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 = ; /// <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[];
try
{
BytesWriter.Write(Checkbit, ref header, );
BytesWriter.Write(RequestId, ref header, );
BytesWriter.Write(Code, ref header, );
BytesWriter.Write(IsEncrypt, ref header, );
BytesWriter.Write(CommandId, ref header, );
BytesWriter.Write(Ext1, ref header, );
}
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, );
RequestId = BytesReader.ReadInt64(ref data, );
Code = BytesReader.ReadInt32(ref data, );
IsEncrypt = BytesReader.ReadBool(ref data, );
CommandId = BytesReader.ReadInt16(ref data, );
Ext1 = BytesReader.ReadInt16(ref data, );
}
catch (Exception ex)
{
LogHepler.Log.ErrorFormat("Deserialize Respone Header Exception. {0}", ex.Message);
}
}
}

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

TCP/IP网络编程之数据包协议的更多相关文章

  1. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

  2. 浅谈TCP/IP网络编程中socket的行为

    我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...

  3. TCP/IP网络编程系列之四(初级)

    TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的 ...

  4. TCP/IP网络编程之多播与广播

    多播 多播方式的数据传输是基于UDP完成的,因此,与UDP服务端/客户端的实现非常接近.区别在于,UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机.换言之,采用多播方式时 ...

  5. TCP/IP网络编程之套接字的多种可选项

    套接字可选项进而I/O缓冲大小 我们进行套接字编程时往往只关注数据通信,而忽略了套接字具有的不同特性.但是,理解这些特性并根据实际需要进行更改也十分重要.之前我们写的程序在创建好套接字后都是未经特别操 ...

  6. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  7. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流(stream)的套接字.TCP是Transmissi ...

  8. 【TCP/IP网络编程】:09套接字的多种可选项

    本篇文章主要介绍了套接字的几个常用配置选项,包括SO_SNDBUF & SO_RCVBUF.SO_REUSEADDR及TCP_NODELAY等. 套接字可选项和I/O缓冲大小 前文关于套接字的 ...

  9. UNIX环境高级编程——TCP/IP网络编程 常用网络信息检索函数

    UNIX环境高级编程——TCP/IP网络编程   常用网络信息检索函数 gethostname()   getppername()   getsockname()   gethostbyname() ...

随机推荐

  1. P3565 由简单的树形dp 引入 长链刨分

    这道题感觉不太行 因为自己没想出来. 先说一下暴力吧,取三个点 让两两之间的距离相等怎么做呢,看起来是很复杂的样子的,但是仔细观察发现 答案出自一个点的儿子之间 或者儿子和父亲之间. 暴力枚举三个点然 ...

  2. Python编程初学者指南PDF高清电子书免费下载|百度云盘

    百度云盘:Python编程初学者指南PDF高清电子书免费下载 提取码:bftd 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计算 ...

  3. 卷积神经网络 part1

    [任务一]视频学习心得及问题总结 根据下面三个视频的学习内容,写一个总结,最后列出没有学明白的问题. [任务二]代码练习 在谷歌 Colab 上完成代码练习,关键步骤截图,并附一些自己的想法和解读. ...

  4. 新老版本vue-cli的安装及创建项目等方式的比较

    vue-cli 3.0 正式版于2018年8月发布,截至到2020年08月05日版本已经更新到4.4.6.Vue CLI 的包名称由 vue-cli 改成了 @vue/cli,目前网上很多的Vue项目 ...

  5. JDBC回顾

    回顾JDBC,完成查询 1 什么是JDBC JDBC(Java DataBase Connectivity)就是Java数据库连接,说白了就是用Java语言来操作数据库.原来我们操作数据库是在控制台使 ...

  6. IIS站点管理-IIS站点以管理员身份或指定用户运行

    PS:概要.背景.结语都是日常“装X”,可以跳过直接看应用程序池设置 环境:Windows Server 2008.阿里云ECS.IIS7.0 概要 IIS应用程序默认情况下,是使用内置帐户运行的,权 ...

  7. 当你的系统依赖于某个bug...

    标题粗略看是有点违反常识的,bug通常是指某些代码存在问题导致系统没有按照期望方式工作,应该是需要尽可能被修复的,这样系统才会正常工作.但是,开发实践中会发现在某些情况下,本来功能没有问题,在你信心满 ...

  8. Java编译解释之cmd

    一.编译 1. javac 类名.java (在类当前目录下) 2. javac 类的全路径 二.解释 1. java 类名(在类当前目录下) 2. java -cp 类的当前目录路径 类名

  9. Java—API/Obiect类的equals toString方法/String类/StringBuffer类/正则表达式

    API  Java 的API(API: Application(应用) Programming(程序) Interface(接口)) 就是JDK中提供给我们使用的类,这些类将底层的代码实现封装了起来 ...

  10. 简单认识Adam优化器

    转载地址 https://www.jianshu.com/p/aebcaf8af76e 基于随机梯度下降(SGD)的优化算法在科研和工程的很多领域里都是极其核心的.很多理论或工程问题都可以转化为对目标 ...