适配器纠错
定义
命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll
一、说明
适配器纠错功能,用于对接收的数据进行纠错。
二、纠错流式单线程数据
我们在适配器介绍中讲过,流式单线程数据的解析,标识和顺位就是解决问题的关键,这也就意味着一般来说,流式单线程数据是不允许有其他数据的。不然数据解析将会发生灾难性故障。
但是有时候,往往环境不是我们所控制的。在数据实际传输时,有极小的情况确实会发生其他数据包混入的情况。
此时,我们就可以使用适配器纠错功能,来解决这种问题。
2.1 纠错原理
对于纠错,我们的目的就是要重新定位到正确数据的起始位置,让数据能够重新解析。那么对于重新定位,我们有以下策略:
- 按照算法找到正确的起始位置。这要求数据格式本身就具有验证性。
- 即时重置缓存。
- 断开连接。
下列,我们将以Tcp数据为例,讲解如何使用适配器纠错功能。
2.2 纠错示例
我们假设一个常见的TLV数据,其格式为:
|Tag|Length|Value|
其中,Tag为4字节,Length为4字节,Value为可变长度的数据。
要解析该数据,我们非常容易的会想到使用模版解析固定包头适配器来解决问题。
所以代码大概如下:
public class MyFixedHeaderCustomDataHandlingAdapter : CustomFixedHeaderDataHandlingAdapter<MyFixedHeaderRequestInfo>
{
/// <summary>
/// 接口实现,指示固定包头长度
/// </summary>
public override int HeaderLength => 8;
/// <summary>
/// 获取新实例
/// </summary>
/// <returns></returns>
protected override MyFixedHeaderRequestInfo GetInstance()
{
return new MyFixedHeaderRequestInfo();
}
}
public class MyFixedHeaderRequestInfo : IFixedHeaderRequestInfo
{
public int Tag { get; set; }
public int Length => this.m_bodyLength;
public byte[] Value { get;private set; }
private int m_bodyLength;
int IFixedHeaderRequestInfo.BodyLength => this.m_bodyLength;
bool IFixedHeaderRequestInfo.OnParsingBody(byte[] body)
{
if (body.Length!=this.m_bodyLength)
{
return false;
}
this.Value = body;
return true;
}
bool IFixedHeaderRequestInfo.OnParsingHeader(byte[] header)
{
if (header.Length!=8)
{
return false;
}
this.Tag = TouchSocketBitConverter.BigEndian.ToInt32(header,0);
this.m_bodyLength = TouchSocketBitConverter.BigEndian.ToInt32(header,4);
return true;
}
}
此时,如果我们的数据是完全符合我们定义的协议的,那么我们的算法就是100%可靠的。
但是,如果我们的数据不符合我们的协议呢?例如:在Tag、Length、Value之间多出n个字节,那么我们的算法就无法 解析了。
此时,我们需要对数据进行处理,进行纠错。
2.3 自我纠错
首先,我们来看能不能自己纠错。自己纠错,必须是数据格式自己能识别错误,并且能自己纠错。在此案例中,明显是不适用的,因为简单的TLV数据是不具备自我纠错能力的。
那么什么样的数据可以自我纠错呢?
例如:|Begin|Tag|TagCrc|Length|LengthCrc|Value|ValueCrc|End|
其中假设:Begin、End是固定的“**和##”,Tag、Length均为4字节,TagCrc、LengthCrc、ValueCrc是对应数据的CRC校验。
那么对于此类数据,可能具有一定纠错能力。
原因是,当在解析数据时,如果我们发现该数据并不是以“**”开头,则会认为该数据是错误的。可以在OnParsingHeader
中返回false
,表示数据错误。然后适配器会向后递推1个字节,再次尝试解析。Tag、Length、Value也可以使用crc校验来决定返回true
或false
。
聪明的小伙伴应该发现了,这种机制也只能解决部分问题。例如:当冗余数据出现在Tag、Length、Value数据间隔时,可能是有用的。一旦冗余数据在Tag、Length、Value之中时,我们仍然无法解析。所以,这种机制可能能解决部分问题。
但是,这仍然是有意义的,因为纠错的目的不仅仅是纠错还原本次数据,更主要的是能解析下次正确的数据。