跳到主要内容
版本:3.0

模板解析“大数据不固定包头”数据适配器

定义

命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll

一、说明

大数据不固定包头,是对不固定的包头模板的补充,一般来是,不固定包头适配器,不能工作于超过2G的数据,但是在少数情况下,会有大量的数据传输需求。所以这部分的业务,可以用大数据不固定包头实现。

二、特点

  1. 可以随意定制数据协议。
  2. 可以与任意语言、框架对接数据。
  3. 可以接收理论无限大的数据。

三、创建适配器

客户端与服务器均适用。下列以服务器为例。

步骤

  1. 声明新建类,实现IBigUnfixedHeaderRequestInfo接口,此对象即为存储数据的实体类,可在此类中声明一些属性,以备使用。
  2. 声明新建类,继承CustomBigUnfixedHeaderDataHandlingAdapter,并且以步骤1声明的类作为泛型。并实现对应抽象方法。
  3. TouchSocketConfig配置中设置。
  4. 通过Received(事件、方法、插件)中的RequestInfo对象,强转为步骤1声明的类型,然后读取其属性值,以备使用。

【MyBigUnfixedHeaderRequestInfo】

首先,新建MyBigUnfixedHeaderRequestInfo类,然后实现IBigUnfixedHeaderRequestInfo用户自定义不固定包头接口。

然后在OnParsingHeader函数执行结束时,对实现的BodyLengthHeaderLength属性作出赋值,以此来决定,后续还应该接收多少数据作为Body

同时最重要的是把ByteBlock.Position属性,设置为适当位置,表示已经消费了多少数据。

class MyBigUnfixedHeaderRequestInfo : IBigUnfixedHeaderRequestInfo
{
/// <summary>
/// 自定义属性,标识数据类型
/// </summary>
public byte DataType { get; set; }

/// <summary>
/// 自定义属性,标识指令类型
/// </summary>
public byte OrderType { get; set; }

/// <summary>
/// 自定义属性,标识实际数据
/// </summary>
public byte[] Body { get; set; }


List<byte> m_bytes = new List<byte>();

private int m_headerLength;
private long m_bodyLength;

#region 接口成员
int IBigUnfixedHeaderRequestInfo.HeaderLength => m_headerLength;

long IBigUnfixedHeaderRequestInfo.BodyLength => m_bodyLength;

void IBigUnfixedHeaderRequestInfo.OnAppendBody(ReadOnlySpan<byte> buffer)
{
//每次追加数据
m_bytes.AddRange(buffer.ToArray());
}

bool IBigUnfixedHeaderRequestInfo.OnFinished()
{
if (m_bytes.Count == this.m_bodyLength)
{
this.Body = m_bytes.ToArray();
return true;
}
return false;
}

bool IBigUnfixedHeaderRequestInfo.OnParsingHeader<TByteBlock>(ref TByteBlock byteBlock)
{
if (byteBlock.CanReadLength<3)//判断可读数据是否满足一定长度
{
return false;
}

var pos= byteBlock.Position;//可以先记录游标位置,当解析不能进行时回退游标

//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
this.m_bodyLength = byteBlock.ReadByte() - 2;
this.DataType = byteBlock.ReadByte();
this.OrderType = byteBlock.ReadByte();


//当执行到这里时,byteBlock.Position已经递增了3个长度。
//所以无需再其他操作,如果是其他,则需要手动移动byteBlock.Position到指定位置。

this.m_headerLength = 3;//表示Header消耗了3个字节,实际上可以省略这一行,但是为了性能,最好加上

return true;
}
#endregion
}

新建MyCustomBigUnfixedHeaderDataHandlingAdapter继承CustomBigUnfixedHeaderDataHandlingAdapter

class MyCustomBigUnfixedHeaderDataHandlingAdapter : CustomBigUnfixedHeaderDataHandlingAdapter<MyBigUnfixedHeaderRequestInfo>
{
protected override MyBigUnfixedHeaderRequestInfo GetInstance()
{
return new MyBigUnfixedHeaderRequestInfo();
}
}

四、使用

private static async Task<TcpClient> CreateClient()
{
var client = new TcpClient();
//载入配置
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:7789")
.SetTcpDataHandlingAdapter(() => new MyCustomBigUnfixedHeaderDataHandlingAdapter())
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个日志注入
}));

await client.ConnectAsync();//调用连接,当连接不成功时,会抛出异常。
client.Logger.Info("客户端成功连接");
return client;
}

private static async Task<TcpService> CreateService()
{
var service = new TcpService();
service.Received = (client, e) =>
{
//从客户端收到信息

if (e.RequestInfo is MyBigUnfixedHeaderRequestInfo myRequest)
{
client.Logger.Info($"已从{client.Id}接收到:DataType={myRequest.DataType},OrderType={myRequest.OrderType},消息={Encoding.UTF8.GetString(myRequest.Body)}");
}
return Task.CompletedTask;
};

await service.SetupAsync(new TouchSocketConfig()//载入配置
.SetListenIPHosts("tcp://127.0.0.1:7789", 7790)//同时监听两个地址
.SetTcpDataHandlingAdapter(() => new MyCustomBigUnfixedHeaderDataHandlingAdapter())
.ConfigureContainer(a =>
{
a.AddConsoleLogger();//添加一个控制台日志注入(注意:在maui中控制台日志不可用)
})
.ConfigurePlugins(a =>
{
//a.Add();//此处可以添加插件
}));
await service.StartAsync();//启动
service.Logger.Info("服务器已启动");
return service;
}
提示

上述创建的适配器客户端与服务器均适用。