---
id: customunfixedheaderdatahandlingadapter
title: 模板解析“非固定包头”数据适配器
---

### 定义

命名空间：TouchSocket.Core <br/>
程序集：[TouchSocket.Core.dll](https://www.nuget.org/packages/TouchSocket.Core)

## 一、说明

有时候，我们需要解析的数据的包头是不定的，例如：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 。

```csharp showLineNumbers
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**。

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

【接收】

```csharp showLineNumbers
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();//启动
```

:::tip 提示

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

:::  


## 四、适配器纠错

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

但有的时候，可能由于其他原因导致后续数据错乱，这时候就需要纠错。详情可看[适配器纠错](./adaptererrorcorrection.mdx)