网络库提供了数据包编码器架构,用于实现各种数据封包,灵活解决粘包问题。常见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; }
}执行结果
自定义编码器
代码