温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

套接字数据包序列化与反序列化方法是什么

发布时间:2021-10-26 10:41:51 来源:亿速云 阅读:156 作者:iii 栏目:编程语言

这篇文章主要介绍“套接字数据包序列化与反序列化方法是什么”,在日常操作中,相信很多人在套接字数据包序列化与反序列化方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”套接字数据包序列化与反序列化方法是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

1.本文背景

经常做C/S,客户端与服务端通信基本是TCP/UDP通信,套接字用得飞起。

比如我们有一个系统,这个系统又分几个系统子模块进程:

  1. C++服务端

  2. Android 客户端

  3. iOS 客户端

  4. WPF桌面管理端 ......

几个模块之间通过TCP或者UDP通信,数据包解析与组装是常规操作,我们定义数据包格式如下:

一个数据包包含包头和包体,定义如下:

包头

序号字段名数据类型备注
1消息标识int用于标识数据包是否合法
2名称string当前消息名称,用于标识数据包类型
3版本号int当前消息版本号,允许程序中消息存在多个版本,用于版本迭代

包含这三个字段:消息标识、名称、版本号,唯一确定消息对象。

包体

序号字段名数据类型备注
1字段1数据类型字段1
2字段2数据类型字段2

包体直接定义字段信息,就像定义类属性一样。

另包头与包体中数据类型定义如下:

数据包字段类型定义

序号数据类型备注
1int4个字节的整型值
2string组成格式:字符串实际值字节长度(2个字节)+字符串实际值byte
3char单字节值
4列表组成格式:4个字节列表长度+列表实际数据值byte
5字典同上,具体后源码

其他数据类型类似,复杂数据类型使用4个字节的值字节长度+实际值byte。

给一个测试数据包

序号字段名数据类型备注
1消息标识int取值:0x4A534604
2消息名称string三国信息,取值:"ThreeCountries"
3版本号int取值:1
4编号int给三国一个编号吧,取值:1
5国名string取值:"蜀国"
6皇帝string取值:"刘备"
7大将个数int5
8大将1编号int取值:1
9大将1名字string取值:"张飞"
10大将1备注string取值:"三板斧"
11大将2编号int取值:2
12大将2名字string取值:"关羽"
13大将2备注string取值:"青龙偃月刀"
14大将3编号int取值:3
15大将3名字string取值:"赵云"
16大将3备注string取值:"很猛的"
17大将4编号int取值:4
18大将4名字string取值:"马超"
19大将4备注string取值:"强"
20大将5编号int取值:5
21大将5名字string取值:"黄忠"
22大将5备注string取值:"老当益壮"

大致理解下:

  • 前三个字段是包体:用于标识整个数据包,便于包体解析;

  • 后面的包体,简单说就是三国中的国家信息简介,前三个字段为三国中的一个国家基本信息:编号、国名、皇帝,后面是该国家大将信息列表,每个大将有编号、名称、备注等。

定义数据对象

根据数据包定义,我们可以很快定义类进行使用,不管你是C++还是Java。下面是我用C#写的对应类,用于序列化与反序列化使用:

/// <summary>
/// 三国
/// </summary>
public class ThreeCountries
{
    /// <summary>
    /// 获取或者设置 ID
    /// </summary>
    public int ID { get; set; }

    /// <summary>
    /// 获取或者设置 国名
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 获取或者设置 皇帝
    /// </summary>
    public string Emperor { get; set; }

    /// <summary>
    /// 获取或者设置 所选课程列表
    /// </summary>
    public List<FamousGeneral> Courses { get; set; }

    public override string ToString()
    {
        return $"三国之一{ID}:{Name}皇帝{Emperor},有 {Courses.Count}名大将";
    }
}

/// <summary>
/// 三国名将
/// </summary>
public class FamousGeneral
{
    /// <summary>
    /// 获取或者设置 编号
    /// </summary>
    public int ID { get; set; }

    /// <summary>
    /// 获取或者设置 名字
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 获取或者设置 描述
    /// </summary>
    public string Memo { get; set; }

    public override string ToString()
    {
        return $"{ID}:{Name}=>{Memo}";
    }
}

对于上面给的数据包你怎么序列化及反序列化?转换成数据如下,下节接着讨论

ThreeCountries shuKingdom = new ThreeCountries
{
    ID = 1,
    Name = "蜀国",
    Emperor = "刘备",
    Courses = new System.Collections.Generic.List<FamousGeneral>
    {
        new FamousGeneral{ ID=1,Name="张飞",Memo="三板斧"},
        new FamousGeneral{ ID=2,Name="关羽",Memo="青龙偃月刀"},
        new FamousGeneral{ ID=3,Name="赵云",Memo="很猛的"},
        new FamousGeneral{ ID=3,Name="马超",Memo="强"},
        new FamousGeneral{ ID=3,Name="黄忠",Memo="老当益壮"},
    }
};

2. 常规操作

序列化

代码太繁琐,我就写个不正规的伪代码吧

定义一个byte数组;
一、写包头
1、写入4字节的消息标识:0x4A534604
计算消息对象名称字符串“ThreeCountries”长度,及转换字符串为byte数组
2、写入2字节的bytes数组长度,写入实际的byte数组值
3、写入4字节的消息版本号
二、写包体
4、写入4字节的大将个数
循环每个大将信息,依次写入
5、写入大将1编号
6、写入大将1名称
7、写入大奖1备注
8、写入大将2编号
9、写入大将3名称
10、写入大奖4备注
...写吐了,省略号

反序列化

不想写了,累

常规操作

定义一个序列化接口,每个网络对象实现其中的序列化与反序列化接口

public interface ISerializeInterface
{
  byte[] Serialize<T>(T t);
  T Deserialize<T>(byte[] arr);
}

public class ThreeCountries : ISerializeInterface
{
  public byte[] Serialize<T>(T t)
  {
    // 将上面的序列化代码写在这
  }
  
  T Deserialize<T>(byte[] arr)
  {
    // 将上面的反序列化代码写在这,不好意思我没写
  }
}

3. 本文操作

写了半天的Demo,文章可能就写的有点水了,我估计读者也不会仔细看代码,直接去Github check项目去了,哈哈。

我还是简单说说吧,实现很简单,定义一些特性,下面红框里的代码文件:

套接字数据包序列化与反序列化方法是什么

使用很简单,在上面的数据类上加上特性,改动不多,看下面代码:

/// <summary>
/// 三国
/// </summary>
[NetObject(Name = "ThreeCountries", Version = 1)]
public class ThreeCountries
{
    /// <summary>
    /// 获取或者设置 ID
    /// </summary>
    [NetObjectProperty(ID = 1)]
    public int ID { get; set; }

    /// <summary>
    /// 获取或者设置 国名
    /// </summary>
    [NetObjectProperty(ID = 2)]
    public string Name { get; set; }

    /// <summary>
    /// 获取或者设置 皇帝
    /// </summary>
    [NetObjectProperty(ID = 3)]
    public string Emperor { get; set; }

    /// <summary>
    /// 获取或者设置 所选课程列表
    /// </summary>
    [NetObjectProperty(ID = 4)]
    public List<FamousGeneral> Courses { get; set; }

    public static NetObjectAttribute CurrentObject = null;

    static ThreeCountries()
    {
        CurrentObject = NetObjectSerializeHelper.GetAttribute<ThreeCountries, NetObjectAttribute>(default(ThreeCountries));
    }

    public override string ToString()
    {
        return $"三国之一{ID}:{Name}皇帝{Emperor},有 {Courses.Count}名大将";
    }
}

/// <summary>
/// 三国名将
/// </summary>
public class FamousGeneral
{
    /// <summary>
    /// 获取或者设置 编号
    /// </summary>
    [NetObjectProperty(ID = 1)]
    public int ID { get; set; }

    /// <summary>
    /// 获取或者设置 名字
    /// </summary>
    [NetObjectProperty(ID = 2)]
    public string Name { get; set; }

    /// <summary>
    /// 获取或者设置 描述
    /// </summary>
    [NetObjectProperty(ID = 3)]
    public string Memo { get; set; }

    public override string ToString()
    {
        return $"{ID}:{Name}=>{Memo}";
    }
}

仔细看的话,只在外层类(ThreeCountries)上加了NetObject特性,和属性上加了NetObjectProperty特性,分别标识消息名称、版本号及每个属性的序列化与反序列化顺序即可,类中使用的子对象Courses属性,也只需要加属性特性即可,如上。

下面添加单元测试,并且测试通过:

套接字数据包序列化与反序列化方法是什么

到此,关于“套接字数据包序列化与反序列化方法是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++
AI