说明

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

实现

首先,实现IFixedHeaderRequestInfo用户自定义固定包头接口,设置BodyLength属性 。

  1. public class MyFixedHeaderRequestInfo : IFixedHeaderRequestInfo
  2. {
  3. private int bodyLength;
  4. /// <summary>
  5. /// 接口实现,标识数据长度
  6. /// </summary>
  7. public int BodyLength
  8. {
  9. get { return bodyLength; }
  10. }
  11. private byte dataType;
  12. /// <summary>
  13. /// 自定义属性,标识数据类型
  14. /// </summary>
  15. public byte DataType
  16. {
  17. get { return dataType; }
  18. }
  19. private byte orderType;
  20. /// <summary>
  21. /// 自定义属性,标识指令类型
  22. /// </summary>
  23. public byte OrderType
  24. {
  25. get { return orderType; }
  26. }
  27. private byte[] body;
  28. /// <summary>
  29. /// 自定义属性,标识实际数据
  30. /// </summary>
  31. public byte[] Body
  32. {
  33. get { return body; }
  34. }
  35. public bool OnParsingBody(byte[] body)
  36. {
  37. if (body.Length == this.bodyLength)
  38. {
  39. this.body = body;
  40. return true;
  41. }
  42. return false;
  43. }
  44. public bool OnParsingHeader(byte[] header)
  45. {
  46. //在该示例中,第一个字节表示后续的所有数据长度,但是header设置的是3,所以后续还应当接收length-2个长度。
  47. this.bodyLength = header[0]-2;
  48. this.dataType = header[1];
  49. this.orderType = header[2];
  50. return true;
  51. }
  52. }

然后继承CustomFixedHeaderDataHandlingAdapter用户自定义固定包头适配器,实现获取实例的方法,即可。

  1. public class MyFixedHeaderCustomDataHandlingAdapter : CustomFixedHeaderDataHandlingAdapter<MyFixedHeaderRequestInfo>
  2. {
  3. public MyFixedHeaderCustomDataHandlingAdapter()
  4. {
  5. this.MaxPackageSize = 1024;
  6. }
  7. /// <summary>
  8. /// 接口实现,指示固定包头长度
  9. /// </summary>
  10. public override int HeaderLength => 3;
  11. /// <summary>
  12. /// 获取新实例
  13. /// </summary>
  14. /// <returns></returns>
  15. protected override MyFixedHeaderRequestInfo GetInstance()
  16. {
  17. return new MyFixedHeaderRequestInfo();
  18. }
  19. }

完工。此时您已获得一个自定义的数据处理适配器。

注意:使用**_CustomDataHandlingAdapter_**及其派生类解析时,接收方收到的数据中,**_ByteBlock_**将为null,同时**_IRequestInfo_**将实现为您自定义的泛型**_TRequestInfo_**

使用自定义适配器

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

对自定义适配器进行单元测试

适配器写完以后,需要经过缜密测试,方才能使用。在RRQM中内置了DataAdapterTester进行模拟、接收的适配器测试类,该测试类能模拟粘包、分包等情况。如果通过,则基本说明能适应99%的情况。具体使用如下:

包长度的工作机制相当于发送固定长度的数据,例如发送方多次发送数据{1,2,3,4,5}。如果设置包长度为10,则在接收时会收到{1,2,3,4,5,1,2,3,4,5},如果包长度设置为7,则会收到{1,2,3,4,5,1,2}{3,4,5,1,2,3,4},以此类推。所以在测试时应当多次设置该值,且最好不要整除发送的数据长度,以模拟更恶劣的环境

【模板适配器测试】

  1. [Theory]
  2. [InlineData(10000, 3)]
  3. [InlineData(10000, 5)]
  4. [InlineData(10000, 200)]
  5. [InlineData(10000, 500)]
  6. [InlineData(10000, 1000)]
  7. public void MyCustomDataHandlingAdapterShouldBeOk(int inputCount, int bufferLength)
  8. {
  9. DataAdapterTester tester = DataAdapterTester.CreateTester(new MyCustomDataHandlingAdapter(), bufferLength);//用BufferLength模拟粘包,分包
  10. ByteBlock block = new ByteBlock();
  11. block.Write((byte)102);//写入数据长度
  12. block.Write((byte)1);//写入数据类型
  13. block.Write((byte)1);//写入数据指令
  14. byte[] buffer = new byte[100];
  15. new Random().NextBytes(buffer);
  16. block.Write(buffer);//写入数据
  17. byte[] data = block.ToArray();
  18. //输出测试时间,用于衡量适配性能
  19. output.WriteLine(tester.Run(data, inputCount, inputCount, 1000 * 2).ToString());
  20. }