字符串消息的序列化

在上一篇文章中,我们使用MQTTnet 框架,实现了一个MQTT服务器、MQTT发布者进程、MQTT订阅者进程。在消息传递过程中,我们将控制台的字符串直接传递。因为MQTT是应用层协议,它是基于TCP协议进行数据传输。我们 直到TCP本身是基于字节流的传输协议。所以我们的字符串最终会 序列化成自己数组进行数据传输。我们先来看下发布者发送消息的代码:

string msg;
while (true)
{
Console.WriteLine("请输入需要发送的消息:");
msg = Console.ReadLine();
msg = "time:" + DateTime.Now.ToString("yyyy-MM-dd HH:MM:ss") + " msg:" + msg;
var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("mytopic")
.WithPayload(msg)
.Build();
var result = await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
Console.WriteLine("成功向MQTT服务器发布消息.....");
}

可以看到WithPayload函数我们直接设置了一个字符串,然后我们来看下MQTT是 如何实现WithPayload这个函数的源代码的,下面代码可以和明显看到,该函数调用了Encoding.UTF8.GetBytes(payload)方法将字符串转换为字节数组,然后再发送。Encoding.UTF8.GetBytes是C#提供了的一个最简单的序列化函数。这种直接将字符串序列化为,这种序列化其实就是文本序列化。在实际的开发中,比如游戏开发,我们通常使用二进制序列化协议而不是文本序列化协议。

public MqttApplicationMessageBuilder WithPayload(string payload)
{
if (string.IsNullOrEmpty(payload))
{
return WithPayload(default(byte[]));
} var payloadBuffer = Encoding.UTF8.GetBytes(payload);
return WithPayload(payloadBuffer);
}

文本序列化和二进制序列化

文本序列化是将对象转换为人类可读的文本格式。常见的文本序列化格式包括JSON、XML和YAML。文本序列化的有点和缺点如下:

优点
1.    可读性:序列化后的数据是人类可读的,便于调试和日志记录。
2.    跨平台:大多数编程语言都支持解析和生成常见的文本格式,如JSON和XML。
3.    灵活性:文本格式可以很容易地进行修改和扩展。
缺点
1.    性能:文本序列化通常比二进制序列化慢,因为需要进行字符串解析和生成。
2.    空间效率:文本格式通常比二进制格式占用更多的存储空间。

二进制序列化是将对象转换为紧凑的二进制格式。常见的二进制序列化格式包括Protocol Buffers、MessagePack和Avro。二进制序列化的优点和缺点如下:

优点
1.    性能:二进制序列化通常比文本序列化快,因为不需要进行字符串解析。
2.    空间效率:二进制格式通常比文本格式占用更少的存储空间。
缺点
1.    可读性:序列化后的数据是二进制的,不易于人类阅读和调试。
2.    跨平台:虽然许多二进制格式是跨平台的,但并不是所有编程语言都支持所有二进制格式。

最终的结论就是:二进制序列化,效率高,速度快,体积小,传输效率高。在实际的 业务开发中,我们基本上都是使用二进制序列化这个方案。当然在本篇文章中,我们 将使用MessagePack这个类库来将对象序列化成二进制字节数组。

MessagePack 是一种高效的二进制序列化格式,和JSON序列化不同的是,MessagePack更快且更小。它由日本工程师 Sadayuki Furuhashi 创建,并在 GitHub 上作为开源项目进行维护。MessagePack 适用于需要高性能和高空间效率的场景,如网络通信、存储和传输大量数据。github开源地址为:https://github.com/MessagePack-CSharp/MessagePack-CSharp。该项目由6.1k的star,是非常优秀的一个二进制序列化开源项目。

接下来,我们就一步步演示如何使用MessagePack这个类库。

定义消息格式

和我们普通的http请求一样,我们可能会封装一个http报文体的通用格式,里面包含三个字段,code,data,message。当然在这里,我们使用同样的Model来承载消息内容。这是一个最简单的C#类,我们不在做详细介绍,大家一看便知。

public class Message<T>
{
public int Code { get; set; }
public string Msg { get; set; }
public T Data { get; set; }
}

安装MessagePack包

要想使用MessagePack框架,我们必须首先安装nuget包:MessagePack。安装命令行如下:

dotnet add package MessagePack

对于承载消息体的对象,我们必须对该类添加MessagePackObject特性,对类中的每一个字段添加Key特性,并且标记好每个字段的序列,一般从0开始,序列依次递增就可以。修改完的消息类如下:

[MessagePackObject]
public class Message<T>
{
[Key(0)]
public int Code { get; set; }
[Key(1)]
public string Msg { get; set; }
[Key(2)]
public T Data { get; set; }
}

消息序列化

MessagePack类库中有一个最基础的类MessagePackSerializer,它提供了两个最基本的函数:SerializeDeserialize分别负责将对象序列化为二进制以及将二进制反序列化为对象。我们使用构建者模式来构建我们最终传输的消息对象。

public class MessageBuilder<T>
{
private int _code;
private string _msg;
private T _data;
public static MessageBuilder<T> Create<T>()
{
return new MessageBuilder<T>();
} public MessageBuilder<T> WithCode(int code)
{
_code = code;
return this;
} public MessageBuilder<T> WithMsg(string msg)
{
_msg = msg;
return this;
} public MessageBuilder<T> WithData(T data)
{
_data = data;
return this;
} public byte[] Build()
{
var message= new Message<T>
{
Code = _code,
Msg = _msg,
Data = _data
};
byte[] bytes = MessagePackSerializer.Serialize(message);
return bytes;
}
}

消息的反序列化

直接使用最基本的Deserialize函数对消息进行反序列化。

public class MessageParser
{
public static Message<T> Parse<T>(byte[] bytes)
{
var message = MessagePackSerializer.Deserialize<Message<T>>(bytes);
return message;
}
}

修改MQTT发布者程序

有了上面的消息序列化和反序列化函数之后,我们就可以直接调用。替换MQTT 发布者程序中的消息序列化过程,这里只挑选核心的部分代码展示:

string data;
while (true)
{
Console.Write("请输入需要发送的消息:");
data = Console.ReadLine();
data = "time:" + DateTime.Now.ToString("yyyy-MM-dd HH:MM:ss") + " msg:" + data;
byte[] bytes = MessageBuilder<string>.Create()
.WithCode(200)
.WithMsg("msg")
.WithData(data)
.Build(); var applicationMessage = new MqttApplicationMessageBuilder()
.WithTopic("mytopic")
.WithPayload(bytes)
.Build();
var result = await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
}

修改MQTT订阅者程序

对于订阅者程序,需要修改接收到消息的回调函数。

var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
mqttClient.ApplicationMessageReceivedAsync += e =>
{
Message<string> message = MessageParser.Parse<string>(e.ApplicationMessage.Payload.First.Span.ToArray());
Console.WriteLine($"接收到消息:clientid:{e.ClientId},topic:{e.ApplicationMessage.Topic},message:{message.Data}");
return Task.CompletedTask;
};

运行结果

接下来,我们依次启动MQTT服务器进程、订阅者进程和发布者进程,然后看运行结果。

首先是服务器进程运行结果:

其次是订阅者:

最后是发布者,我们用发布者发布三条消息,并且订阅者成功收到了消息。

MQTT消息传递过程中,序列化协议如何选择?文本序列化还是二进制序列化协议。的更多相关文章

  1. 10-vim-选中命令-01-三种选择文本的方式

    选择文本(可视模式) 学习复制命令前,应该先学会如何选中要复制的代码. 在vi中要选择文本,需要先使用visual命令切换到可视模式. vi中提供了三种可视模式. 按ESC可以放弃选中,返回到命令模式 ...

  2. VS2017安装过程中【工作负载】选择安装

    Visual Studio 2017安装和12.13.15安装过程一样的,只是17安装的时候 选择的[工作负载]不同了,小编安装17的目的是为了安装“.Net Core跨平台开发” 这个模块,当然,其 ...

  3. 网络直播流媒体协议的选择讨论,RTSP,RTMP,HTTP,私有协议?

    最近有不少人在EasyDarwin的交流群里面问关于花椒.映客手机直播技术的问题,还有RTSP.RTMP协议选择的问题,这里个人谈一下自己的愚见. 1.不管是RTSP/RTP.RTMP.HTTP,亦或 ...

  4. win10和Ubuntu双系统安装过程中遇到的问题

    1.安装过程 注意分区问题,很重要 https://blog.csdn.net/baobei0112/article/details/77996570 https://blog.csdn.net/s7 ...

  5. C#中的二进制序列化和Json序列化

    序列化就是把一个对象变成流的形式,方便传输和还原.小弟不才,总结下对二进制序列化和Json序列化的使用: 1.首先,二进制序列化(BinaryFormatter)要求要序列化的类必须是可序列化的(即在 ...

  6. 开源!一款功能强大的高性能二进制序列化器Bssom.Net

    好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...

  7. .NET 二进制序列化器,SOAP序列化器,XML序列化器

    这里就不说JSON序列化了,只介绍三种:二进制序列化器,SOAP序列化器,XML序列化器 直接上代码: /// <summary> /// 二进制序列化器. /// 最节省流量,压缩程度最 ...

  8. WCF技术剖析之十三:序列化过程中的已知类型(Known Type)

    原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...

  9. eclipse javaEE版下载过程中选择镜像(Select Another Mirror)无反应解决办法,附带eclipse javaEE版下载教程。

    1.eclipse javaEE版下载过程中选择镜像(Select Another Mirror)无反应 (复制该网址下载即可 https://mirrors.neusoft.edu.cn/eclip ...

  10. DHT协议C++实现过程中各种问题

    ---恢复内容开始--- 博主是个菜鸟,什么地方写的不对或者理解有误的地方还请指正出来. DHT协议是BT协议中的一部分,也是一个辅助性的协议.HTTP协议中用 来定位资源也就是html文本是用URL ...

随机推荐

  1. 无网环境Docker Rpm离线安装

    总体思路:找一台可以联网的linux,下载docker的RPM依赖包而不进行安装(yum localinstall),将所有依赖的rpm环境打包好,再在无网环境中解压逐一安装(rpm: --force ...

  2. JDK8之前日期时间API

    /*java.util.Date类---java.sql.Date类1.两个构造器的使用 >构造器一:Date():创建一个对应当前时间的Date对象 >构造器二:创建指定毫秒数的Date ...

  3. CentOS 7 MongoDB 重装启动失败

    诊断过程 1. 错误提示: > journalctl -xe 提示: .... ERROR: child process failed, exited with error number 14 ...

  4. CP56Time2A时间转换

    * CP56Time2A时间格式 该时标格式使用7个字节来表示时间信息,上图的表中体现为--从最左侧8所在的行开始,到下面56所在的行,共7行.每一行表示一个字节,每行从右向左依次是该字节的第一位(最 ...

  5. Python内存管理机制和垃圾回收机制的简单理解

    一.内存管理机制 1.由c开发出来的cpython 2.include / objests 3.需要下载python源码包 4.Pyobject:float PyVarObject: 5.在pytho ...

  6. 百思不得其解,DeepSeek怎么突然就比肩GPT了?

    >关注公众号**回复1**>>获取**一线.总监.高管<管理秘籍>** 之前大家都认为中美在AI领域的差距很大,谁曾想春节期间**DeepSeek横空出世**,直接给Op ...

  7. linux监控系统行为

    1.验证电脑是否存在,一般都有 which script /usr/bin/script 2.配置profile文件,在末尾添加如下内容: vim /etc/profile ============= ...

  8. C盘扩展卷碰到的那些事-->不是同一块物理磁盘操作扩展卷是有坑的

    自己电脑上面用过win10系统资源管理器扩展卷的功能,用过几次都成功扩容了磁盘空间,简单说一下原理: 就是将剩余未分配的磁盘空间划给要扩展的磁盘. 这天公司的电脑C盘老是红色提示空间不足,那就扩充容量 ...

  9. Linux环境Docker使用代理推拉镜像

    闲扯几句 不知不觉已经2月中了,1个半月忙得没写博客,这篇其实很早就想写了(可追溯到Docker刚刚无法拉镜像的时候),由于工作和生活上的事比较多又在备考软考架构,拖了好久-- 简单记录下怎么做的,以 ...

  10. N-gram基本原理

    N-gram模型是一种语言模型(Language Model,LM),语言模型是一个基于概率的判别模型,它的输入是一句话(单词的顺序序列),输出是这句话的概率,即这些单词的联合概率(joint pro ...