使用protobuf-net进行部分和异步反序列化c#

时间:2013-08-16 07:52:02

标签: c# protobuf-net

上下文

我有一个具有以下结构的文件:

[ProtoContract]
public class Data
{
    [ProtoMember(1)]
    public string Header { get; set; }

    [ProtoMember(2)]
    public byte[] Body { get; set; }
}

将数据读取/写入文件的代码在asp.net mvc webapi上下文中运行。我正在尝试保持每个阻塞IO异步以最小化阻塞并实现最佳可伸缩性。从文件读取和写入确实支持ReadAsync,WriteAsync和CopyToAsync。

正文可能相当大(>>标题),如果标题符合某些特定条件,我只需要读取正文。

我可以同步部分阅读和反序列化标题,并使用Deserialize part of a binary file

中解释的方法以相同的方式读取和反序列化正文

问题

如何使用异步文件IO完全相同,读取和反序列化标头异步并以相同的方式读取和反序列化主体?<​​/ p>

我看过Asynchronous protobuf serialization不是一个选项。

1 个答案:

答案 0 :(得分:2)

技术上protobuf字段可能是乱序的,但在大多数情况下(包括你展示的那个)我们可以合理地假设这些字段是有序的(这里将它们排除在外的唯一方法是单独序列化两个半类并连接结果,这在protobuf规范中在技术上是有效的。)

因此;我们将拥有的是:

  • varint表示:field 1,string - always decimal 10
  • 表示“a”的varint,标题的长度
  • “a”字节,UTF-8编码标题
  • varint表示:字段2,字符串 - 始终为小数18
  • 表示“b”的varint,身体的长度
  • “b”字节,正文

我们可以假设“a”是>= 0< int.MaxValue - 这意味着它最多需要5个字节来编码;因此,如果缓冲至少 6个字节,您将获得足够的信息来了解标头的大小。当然,它在技术上也可以包含身体的一部分,所以你需要保持它!但是,如果您有异步同步Stream,则可以通过以下方式读取 流的那部分:

int protoHeader = ProtoReader.DirectReadVarintInt32(stream); // 10
int headerLength = ProtoReader.DirectReadVarintInt32(stream);
string header = ProtoReader.DirectReadString(stream, headerLength);

或者,如果“异步同步”很棘手,那么明确阅读:

static byte[] ReadAtLeast6()
{
    return new byte[] { 0x0A, 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F };
}
static byte[] ReadMore(int bytes)
{
    return new byte[] { 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64 };
}
static void Main()
{
    // pretend we read 7 bytes async
    var data = ReadAtLeast6();
    using (var ms = new MemoryStream())
    {
        ms.Write(data, 0, data.Length);
        ms.Position = 0;
        int protoHeader = ProtoReader.DirectReadVarintInt32(ms); // 10
        int headerLength = ProtoReader.DirectReadVarintInt32(ms); // 11

        int needed = (headerLength + (int)ms.Position) - data.Length; // 6 more
        var pos = ms.Position;
        ms.Seek(0, SeekOrigin.End);
        data = ReadMore(needed);
        ms.Write(data, 0, needed);
        ms.Position = pos;
        string header = ProtoReader.DirectReadString(ms, headerLength);
    }
}