网络库提供了数据包编码器架构,用于实现各种数据封包,灵活解决粘包问题。常见SRMP标准封包、固定长度封包、字符分隔封包等,具体应用有MQTT和RocketMQ协议实现。
Nuget包:NewLife.Core
源码地址:https://github.com/NewLifeX/X/blob/master/NewLife.Core/Model/IHandler.cs
Get Started
新建NET7控制台项目,并从Nuget引入 NewLife.Core,写入以下代码:
using NewLife; using NewLife.Data; using NewLife.Log; using NewLife.Net; using NewLife.Net.Handlers; using NewLife.Serialization; XTrace.UseConsole(); var server = new NetServer { Port = 12345, Log = XTrace.Log, SessionLog = XTrace.Log, //SocketLog = XTrace.Log, //LogSend = true, //LogReceive = true }; server.Add<LengthFieldCodec>(); server.Received += (s, e) => { XTrace.WriteLine("原始:{0}", e.Packet.ToHex(32, "-")); if (e.Message is Packet pk) XTrace.WriteLine("收到:{0}", pk.ToStr()); }; server.Start(); var uri = new NetUri("tcp://127.0.0.1:12345"); var client = uri.CreateRemote(); client.Log = XTrace.Log; client.Add<LengthFieldCodec>(); client.Open(); var str = "Stone"; var pk = new Packet(str.GetBytes()); client.SendMessage(pk); var info = new LoginInfo { UserName = "Stone", Password = "NewLife" }; pk = new Packet(info.ToJson().GetBytes()); client.SendMessage(pk); Console.ReadLine(); class LoginInfo { public String UserName { get; set; } public String Password { get; set; } }
执行结果
这是一个添加了长度封包编码器 LengthFieldCodec 的网络通信例程,客户端发送了两次消息,SendMessge内部经过编码器对消息进行编码,服务端的编码器对消息进行解码。
- 发送字符串Stone,本应只有5个字节,服务端收到7个字节,开头多了 05-00,即后续数据长度5字节。
- 发送一段Json字符串,服务端收到数据头部多了 29-00(0x0029的小端字节序),即后续数据长度41字节
由此可见,数据封包编码器,本质上就是在消息体外部再包装一层,增加一些标识以便于接收方判断消息体如何拆分,处理粘包问题。
长度封包编码器 LengthFieldCodec
常见协议使用指定字段表示负载数据长度。如上述例程。
可用属性项如下:
- Offset。长度所在位置,一般为0
- Size。长度占据字节数,1/2/4个字节,0表示压缩编码整数,默认2
- Expire。过期时间,超过该时间后按废弃数据处理,默认500ms
长度是2字节时,可传输最大字节数限制在65535,因此有些协议使用4字节。硬件通信时更多使用1个字节。
标准封包编码器StandardCodec
功能
代码
using NewLife; using NewLife.Data; using NewLife.Log; using NewLife.Net; using NewLife.Net.Handlers; using NewLife.Serialization; XTrace.UseConsole(); var server = new NetServer { Port = 12345, Log = XTrace.Log, SessionLog = XTrace.Log, }; server.Add<StandardCodec>(); server.Received += (s, e) => { XTrace.WriteLine("原始:{0}", e.Packet.ToHex(32, "-")); if (e.Message is Packet pk) XTrace.WriteLine("收到:{0}", pk.ToStr()); }; server.Start(); var uri = new NetUri("tcp://127.0.0.1:12345"); var client = uri.CreateRemote(); client.Log = XTrace.Log; client.Add<StandardCodec>(); client.Open(); var str = "Stone"; var pk = new Packet(str.GetBytes()); client.SendMessage(pk); var info = new LoginInfo { UserName = "Stone", Password = "NewLife" }; pk = new Packet(info.ToJson().GetBytes()); client.SendMessage(pk); Console.ReadLine(); class LoginInfo { public String UserName { get; set; } public String Password { get; set; } }
执行结果
自定义编码器
代码