跳到主要内容
版本:2.0.0

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

定义

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

一、说明

有时候,我们需要解析的数据的包头是不定的,例如:HTTP数据格式,其数据头和数据体由“\r\n”隔开,而数据头又因为请求者的请求信息的不同,头部数据量不固定,而数据体的长度,也是由数据头的ContentLength的值显式指定的,所以,可以考虑使用CustomUnfixedHeaderDataHandlingAdapter解析。

二、特点

  1. 可以自由适配所有的数据协议。
  2. 可以随意定制数据协议。
  3. 可以与任意语言、框架对接数据。

三、使用

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

步骤

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

【MyUnfixedHeaderRequestInfo】 首先,新建MyFixedHeaderRequestInfo类,然后实现IUnfixedHeaderRequestInfo用户自定义固定包头接口。 然后在OnParsingHeader函数执行结束时,对实现的BodyLength属性作出赋值,以此来决定,后续还应该接收多少数据作为Body 。

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


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(ByteBlock byteBlock)
{
//此处逻辑和固定包头模板基本一致。也需要对BodyLength作出赋值。
//但是不同固定包头的是,需要自己移动已读的游标。并且返回正确的值。

//下列通过假逻辑实现Http协议的解析。

//从现有的可读数据中,读取“\r\n\r\n”,以此来分割http的headers和body。
int index = byteBlock.Buffer.IndexOfFirst(byteBlock.Pos, byteBlock.CanReadLen, Encoding.UTF8.GetBytes("\r\n\r\n"));
if (index > 0)
{
//索引到了“\r\n\r\n”,以此推断出,在当前缓存区中,由byteBlock.Pos至index的数据即为headers。
int headerLength = index - byteBlock.Pos;
string headers = Encoding.UTF8.GetString(byteBlock.Buffer, byteBlock.Pos,headerLength);//解码headers
byteBlock.Pos += headerLength;//然后将缓存区的游标移至headers结束的位置。

//最后通过headers,解析出后续的body的长度,然后在此处赋值。
this.bodyLength = 0;//此处的0是占位值。
return true;
}
else
{
//没索引到“\r\n\r\n”,以此推断出,在当前缓存区中,header的接收尚未完成,
//所以返回false,并且不需要修改游标。
return false;
}
}
}


新建MyCustomUnfixedHeaderDataHandlingAdapter继承CustomUnfixedHeaderDataHandlingAdapter

public class MyCustomUnfixedHeaderDataHandlingAdapter : CustomUnfixedHeaderDataHandlingAdapter<MyUnfixedHeaderRequestInfo>
{
protected override MyUnfixedHeaderRequestInfo GetInstance()
{
return new MyUnfixedHeaderRequestInfo();
}
}

【接收】

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

service.Setup(new TouchSocketConfig()//载入配置
.SetListenIPHosts(new IPHost[] { new IPHost(7790) })
.SetTcpDataHandlingAdapter(() => { return new MyCustomUnfixedHeaderDataHandlingAdapter(); }));//配置适配器

service.Start();//启动
提示

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

四、适配器纠错

该适配器当收到半包数据时,会自动缓存半包数据,然后等后续数据收到以后执行拼接操作。

但有的时候,可能由于其他原因导致后续数据错乱,这时候就需要纠错。详情可看适配器纠错