跳到主要内容
版本:1.3.9

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

一、说明

和用户自定义适配器相比,使用模板解析将会更加简单流程。例如在上节所说的数据格式,前三个字节是固定长度的,为3,而后续长度则由第一个字节计算可得,所以我们把类似这样的数据格式,叫做“固定包头”数据,那么,他就可以使用固定包头数据解析模板。

二、特点

  1. 可以自由适配**99%**的数据协议(例如:modbus,电力控制协议等)。
  2. 可以随意定制数据协议。
  3. 可以与任意语言、框架对接数据。

三、使用

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

步骤

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

【MyFixedHeaderRequestInfo】

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

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

public class MyFixedHeaderRequestInfo : IFixedHeaderRequestInfo
{
private int bodyLength;
/// <summary>
/// 接口实现,标识数据长度
/// </summary>
public int BodyLength
{
get { return bodyLength; }
}

private byte dataType;
/// <summary>
/// 自定义属性,标识数据类型
/// </summary>
public byte DataType
{
get { return dataType; }
}

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

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


public bool OnParsingBody(byte[] body)
{
if (body.Length == this.bodyLength)
{
this.body = body;
return true;
}
return false;
}


public bool OnParsingHeader(byte[] header)
{
//在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
this.bodyLength = header[0]-2;
this.dataType = header[1];
this.orderType = header[2];
return true;
}
}

新建MyFixedHeaderCustomDataHandlingAdapter继承CustomFixedHeaderDataHandlingAdapter,然后对HeaderLength作出赋值,以此表明固定包头的长度是多少。

public class MyFixedHeaderCustomDataHandlingAdapter : CustomFixedHeaderDataHandlingAdapter<MyFixedHeaderRequestInfo>
{
/// <summary>
/// 接口实现,指示固定包头长度
/// </summary>
public override int HeaderLength => 3;

/// <summary>
/// 获取新实例
/// </summary>
/// <returns></returns>
protected override MyFixedHeaderRequestInfo GetInstance()
{
return new MyFixedHeaderRequestInfo();
}
}

【接收】

TcpService service = new TcpService();
service.Received += (client, byteBlock, requestInfo) =>
{
//接收信息,在CustomDataHandlingAdapter派生的适配器中,byteBlock将为null,requestInfo将为适配器定义的泛型
if (requestInfo is MyFixedHeaderRequestInfo myRequestInfo)
{
//此处可以处理MyFixedHeaderRequestInfo的相关信息了。
string body = Encoding.UTF8.GetString(myRequestInfo.Body, 0, myRequestInfo.Body.Length);
}

};

service.Setup(new TouchSocketConfig()//载入配置
.SetListenIPHosts(new IPHost[] { new IPHost(7790) })
.SetDataHandlingAdapter(() => { return new MyFixedHeaderCustomDataHandlingAdapter(); }))//配置适配器
.Start();//启动
提示

上述示例的数据是经典的固定包头格式,具有Header+Body的明显分割点。但有时候,也有一些数据有好几段,例如:具有Crc校验的数据,也就是Header+Body+Crc的格式,这时候,我们可以把Body+Crc看做一段数据,然后从Header解析BodyLength以后,加上Crc的长度。最后会在OnParsingBody时,将Body和Crc一起投递,届时做好数据分割即可。

提示

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