实现你的 PipelineFilter

Keywords: PipelineFilter

内置的 PipelineFilter 模版

阅读了前面一篇文档之后, 你可能会觉得用 SuperSocket 来实现你的自定义协议并不简单。但事实上,由于 SuperSocket 已经提供了一些内置的 PipelineFilter 模版,这些几乎可以覆盖 90% 的场景的模版极大的简化了你的开发工作。所以你不需要完全从头开始实现 PipelineFilter。即使这些内置的模版无法满足你的需求,完全自己实现PipelineFilter也不是难事

SuperSocket 提供了这些 PipelineFilter 模版:

如何基于内置模版实现 PipelineFilter

FixedHeaderPipelineFilter - 头部格式固定并且包含内容长度的协议

这种协议将一个请求定义为两大部分, 第一部分定义了包含第二部分长度等等基础信息. 我们通常称第一部分为头部.

例如, 我们有一个这样的协议: 头部包含 3 个字节, 第 1 个字节用于存储请求的类型, 后两个字节用于代表请求体的长度:

/// +-------+---+-------------------------------+
/// |request| l |                               |
/// | type  | e |    request body               |
/// |  (1)  | n |                               |
/// |       |(2)|                               |
/// +-------+---+-------------------------------+

根据此协议的规范, 我们可以使用如下代码定义包的类型:

public class MyPackage
{
    public byte Key { get; set; }

    public string Body { get; set; }
}

下一个是设计PipelineFilter:

public class MyPipelineFilter : FixedHeaderPipelineFilter<MyPackage>
{
    public MyPipelineFilter()
        : base(3) // 包头的大小是3字节,所以将3传如基类的构造方法中去
    {

    }

    // 从数据包的头部返回包体的大小
    protected override int GetBodyLengthFromHeader(ref ReadOnlySequence<byte> buffer)
    {
        var reader = new SequenceReader<byte>(buffer);
        reader.Advance(1); // skip the first byte
        reader.TryReadBigEndian(out short len);
        return len;
    }

    // 将数据包解析成 MyPackage 的实例
    protected override MyPackage DecodePackage(ref ReadOnlySequence<byte> buffer)
    {
        var package = new MyPackage();

        var reader = new SequenceReader<byte>(buffer);

        reader.TryRead(out byte packageKey);
        package.Key = packageKey;            
        reader.Advance(2); // skip the length             
        package.Body = reader.ReadString();

        return package;
    }
}

最后,你可通过数据包的类型和 PipelineFilter 的类型来创建宿主:

var host = SuperSocketHostBuilder.Create<MyPackage, MyPipelineFilter>()
    .UsePackageHandler(async (s, p) =>
    {
        // handle your package over here
    }).Build();

你也可以通过将解析包的代码从 PipelineFilter 移到 你的包解码器中来获得更大的灵活性:

public class MyPackageDecoder : IPackageDecoder<MyPackage>
{
    public MyPackage Decode(ref ReadOnlySequence<byte> buffer, object context)
    {
        var package = new MyPackage();

        var reader = new SequenceReader<byte>(buffer);

        reader.TryRead(out byte packageKey);
        package.Key = packageKey;            
        reader.Advance(2); // skip the length             
        package.Body = reader.ReadString();

        return package;
    }
}

通过 host builder 的 UsePackageDecoder 方法来在SuperSocket中启用它:

builder.UsePackageDecoder<MyPackageDecoder>();