---
id: datahandleadapter
title: 原始自定义适配器
---


## 说明

**自定义适配器可从两个方面入手。**

1. 则是直接从DataHandlingAdapter继承，此时可以接触到最原始的TCP数据，可以自定实现数据的继续投递方式。但一般实现算法比较困难，因为所考虑的情况比较多。
2. 则是从CustomDataHandlingAdapter（用户快捷自定义适配器）继承，此时数据的投递必须通过IRequestInfo，ByteBlock将为null。所需考虑的情况比较单一，对于数据的处理也比较简单。 <a name="TwJUE"></a>

## 原始DataHandlingAdapter

自己实现适配器，然后使其工作。例如：假设如下数据格式，第一个字节表示整个数据长度（包括数据类型和指令类型），第二字节表示数据类型，第三字节表示指令类型，后续字节表示其他数据。

> 其次，希望在发送时，只传入数据类型，指令类型和其他数据，而数据长度则由适配器自行封装。最后，希望在接收端每次能接收到一个完整的数据。

<img src={require('@site/static/img/docs/datahandleadapter-1.png').default}/>

## 实现

首先，创建类，继承自`DataHandlingAdapter`，然后实现对应属性，及方法。

```csharp
class MyDataHandleAdapter : DataHandlingAdapter
{
    public override bool CanSplicingSend => false;

    public override bool CanSendRequestInfo => false;

    protected override void PreviewReceived(ByteBlock byteBlock)
    {
       
    }

    protected override void PreviewSend(byte[] buffer, int offset, int length)
    {
       
    }

    protected override void PreviewSend(IRequestInfo requestInfo)
    {
        
    }

    protected override void PreviewSend(IList<ArraySegment<byte>> transferBytes)
    {
        
    }

    protected override void Reset()
    {
        
    }
}
```

【封装发送数据长度】
封装发送数据时，比较简单，示例如下：

```csharp
protected override void PreviewSend(byte[] buffer, int offset, int length)
{
    int dataLen = length - offset;//先获取需要发送的实际数据长度
    if (dataLen > byte.MaxValue)//超长判断
    {
        throw new OverlengthException("发送数据太长。");
    }

    //从内存池申请内存块，因为此处数据绝不超过255，所以避免内存池碎片化，每次申请64K
    //ByteBlock byteBlock = new ByteBlock(dataLen+1);//实际写法。
    using (ByteBlock byteBlock = new ByteBlock(64 * 1024))
    {
        byteBlock.Write((byte)dataLen);//先写长度
        byteBlock.Write(buffer, offset, length);//再写数据
        this.GoSend(byteBlock.Buffer, 0, byteBlock.Len);
    }
}
```

【解析接收数据】
从原生适配器解封数据，需要考虑的情况比较多。在本示例中，需要考虑以下情况：

1. 一次刚好接收一个数据。
2. 一次刚好接收了多个数据。
3. 一次接收了多个数据，但最后一个数据不完整。
4. 一次未接收完一个数据。

综上，情况比较复杂，所以就必须自己做接收数据的缓存容器。

```csharp
/// <summary>
/// 临时包，此包仅当前实例储存
/// </summary>
private ByteBlock tempByteBlock;

/// <summary>
/// 包剩余长度
/// </summary>
private byte surPlusLength;
protected override void PreviewReceived(ByteBlock byteBlock)
{
    byte[] buffer = byteBlock.Buffer;
    int r = byteBlock.Len;
    if (this.tempByteBlock == null)//如果没有临时包，则直接分包。
    {
        SplitPackage(buffer, 0, r);
    }
    else
    {
        if (surPlusLength == r)//接收长度正好等于剩余长度，组合完数据以后直接处理数据。
        {
            this.tempByteBlock.Write(buffer, 0, surPlusLength);
            PreviewHandle(this.tempByteBlock);
            this.tempByteBlock = null;
            surPlusLength = 0;
        }
        else if (surPlusLength < r)//接收长度大于剩余长度，先组合包，然后处理包，然后将剩下的分包。
        {
            this.tempByteBlock.Write(buffer, 0, surPlusLength);
            PreviewHandle(this.tempByteBlock);
            this.tempByteBlock = null;
            SplitPackage(buffer, surPlusLength, r);
        }
        else//接收长度小于剩余长度，无法处理包，所以必须先组合包，然后等下次接收。
        {
            this.tempByteBlock.Write(buffer, 0, r);
            surPlusLength -= (byte)r;
        }
    }
}
/// <summary>
/// 分解包
/// </summary>
/// <param name="dataBuffer"></param>
/// <param name="index"></param>
/// <param name="r"></param>
private void SplitPackage(byte[] dataBuffer, int index, int r)
{
    while (index < r)
    {
        byte length = dataBuffer[index];
        int recedSurPlusLength = r - index - 1;
        if (recedSurPlusLength >= length)
        {
            ByteBlock byteBlock = new ByteBlock(length);
            byteBlock.Write(dataBuffer, index + 1, length);
            PreviewHandle(byteBlock);
            surPlusLength = 0;
        }
        else//半包
        {
            this.tempByteBlock = new ByteBlock(length);
            surPlusLength = (byte)(length - recedSurPlusLength);
            this.tempByteBlock.Write(dataBuffer, index + 1, recedSurPlusLength);
        }
        index += length + 1;
    }
}
/// <summary>
/// 处理数据
/// </summary>
/// <param name="byteBlock"></param>
private void PreviewHandle(ByteBlock byteBlock)
{
    try
    {
        this.GoReceived(byteBlock, null);
    }
    finally
    {
        byteBlock.Dispose();//在框架里面将内存块释放
    }
}
```

## 使用自定义适配器

自定义适配器的使用和预设的适配器一样。不过在该案例中，发送数据时，应当传入三个有效值，分别为**数据类型**，**指令类型**，**其他数据**。 <a name="V3gQW"></a>


## 封装函数及分片发送意义

在上述案例中，发送数据时应当传入**数据类型**，**指令类型**，**其他数据**三个有效值，而在RRQM中，发送函数仅有Send（和重载），这无疑需要我们自己封装`其他方法`。
***假设以下情况需要实现：***

> 数据类型有两种，分别为Up（1），Down（0）。指令类型有两种，分别为Go（1）、Hold（0）。数据类型和指令类型可以任意组合，且均可携带其他数据。

面对上述情况，我们可以封装以下函数使用：

```csharp
public class MySocketClient : SimpleSocketClient
{
    public void Up_Go_Send(byte[] data)
    {
        ByteBlock byteBlock = new ByteBlock(this.BufferLength);//内存池实现，可以直接new byte[].
        byteBlock.Write((byte)1);
        byteBlock.Write((byte)1);
        byteBlock.Write(data);
        try
        {
            this.Send(byteBlock);
        }
        finally
        {
            byteBlock.Dispose();
        }
    }
    public void Down_Go_Send(byte[] data)
    {
        ByteBlock byteBlock = new ByteBlock(this.BufferLength);//内存池实现，可以直接new byte[].
        byteBlock.Write((byte)0);
        byteBlock.Write((byte)1);
        byteBlock.Write(data);
        try
        {
            this.Send(byteBlock);
        }
        finally
        {
            byteBlock.Dispose();
        }
    }
    public void Up_Hold_Send(byte[] data)
    {
        ByteBlock byteBlock = new ByteBlock(this.BufferLength);//内存池实现，可以直接new byte[].
        byteBlock.Write((byte)1);
        byteBlock.Write((byte)0);
        byteBlock.Write(data);
        try
        {
            this.Send(byteBlock);
        }
        finally
        {
            byteBlock.Dispose();
        }
    }
    public void Down_Hold_Send(byte[] data)
    {
        ByteBlock byteBlock = new ByteBlock(this.BufferLength);//内存池实现，可以直接new byte[].
        byteBlock.Write((byte)0);
        byteBlock.Write((byte)0);
        byteBlock.Write(data);
        try
        {
            this.Send(byteBlock);
        }
        finally
        {
            byteBlock.Dispose();
        }
    }
}
```

***为什么要分片发送？？***

> 在示例代码中不难看出，封装的函数将**发送数据**进行了**Write**操作（相当于Copy），这无疑是消耗性能的。只是在该案例中，复制的数据最大为255，感觉优化效果甚微，倘若我们需要发送的数据是1Mb，那就相当于在封装数据时，因为**前两个字节**的存在而复制1Mb的数据（冤死了），然后在适配器中还需要因为**数据包头**，再复制一次。

***优化。。。***
所以我们在封装时，可以使用分片发送，但是同时也需要适配器支持。不然内部会出错。

```csharp
protected override void PreviewSend(IList<ArraySegment<byte>> transferBytes)
{
    int dataLen = 0;
    foreach (var item in transferBytes)
    {
        dataLen += item.Count;
    }
    if (dataLen > byte.MaxValue)//超长判断
    {
        throw new OverlengthException("发送数据太长。");
    }

    //从内存池申请内存块，因为此处数据绝不超过255，所以避免内存池碎片化，每次申请64K
    //ByteBlock byteBlock = new ByteBlock(dataLen+1);//实际写法。

    using (ByteBlock byteBlock = new ByteBlock(64 * 1024))
    {
        byteBlock.Write((byte)dataLen);//先写长度
        foreach (var item in transferBytes)
        {
            byteBlock.Write(item.Array, item.Offset, item.Count);//依次写入
        }
        this.GoSend(byteBlock.Buffer, 0, byteBlock.Len);
    }
}
```

***重新封装函数。。。***

```csharp
public void Up_Go_SplicingSend(byte[] data)
{
    List<TransferByte> transferBytes = new List<TransferByte>();
    transferBytes.Add(new TransferByte(new byte[] { 1}));
    transferBytes.Add(new TransferByte(new byte[] { 1}));
    transferBytes.Add(new TransferByte(data));
    this.Send(transferBytes);
}
public void Down_Go_SplicingSend(byte[] data)
{
    List<TransferByte> transferBytes = new List<TransferByte>();
    transferBytes.Add(new TransferByte(new byte[] { 0 }));
    transferBytes.Add(new TransferByte(new byte[] { 1 }));
    transferBytes.Add(new TransferByte(data));
    this.Send(transferBytes);
}
public void Up_Hold_SplicingSend(byte[] data)
{
    List<TransferByte> transferBytes = new List<TransferByte>();
    transferBytes.Add(new TransferByte(new byte[] { 1 }));
    transferBytes.Add(new TransferByte(new byte[] { 0 }));
    transferBytes.Add(new TransferByte(data));
    this.Send(transferBytes);
}
public void Down_Hold_SplicingSend(byte[] data)
{
    List<TransferByte> transferBytes = new List<TransferByte>();
    transferBytes.Add(new TransferByte(new byte[] { 0 }));
    transferBytes.Add(new TransferByte(new byte[] { 0 }));
    transferBytes.Add(new TransferByte(data));
    this.Send(transferBytes);
}
```
