首页 > 解决方案 > 如何检测 PipeReader 何时到达数据源的末端(管道末端)?

问题描述

我们可以调用 ReadAsync() 并检查缓冲区中的读取字节...

PipeReader reader = ...;
ReadResult readResult = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = readResult.Buffer;
long availableBytes = buffer.Length

如果在调用 ReadAsync 之前长度没有增加,这是否表明管道结束(没有更多字节要读取)?如果不是,那么检测“管道末端”的正确方法是什么?

我们可以像这样表示缓冲区中字节的消耗:

reader.AdvanceTo(count);

然后检查是否有任何未消耗的字节,或者未来提供字节的可能性(即生产者尚未发出信号,它已停止向管道添加新字节)

readResult.IsCompleted

但是,如果我在缓冲区中寻找一个(或多个)序列,并在使用它之前等待一个完整的序列,那么即使缓冲区包含所有可用字节并且生产者已发出完成信号,IsComplete 似乎仍然为假。

谢谢。

标签: .netsystem.io.pipelines

解决方案


.IsCompleted确实表示管道的末端,在套接字等被关闭的意义上(而不是打开但现在没有更多数据);我希望这里发生的是你:

  • 获取读取缓冲区
  • 读取整个缓冲区寻找一个序列,但没有找到它
  • 因此处理零字节
  • 因此说.AdvanceTo(zero)

还有一个重要的第二个重载AdvanceTo——你不应该只告诉它你了什么;你应该告诉它你检查了什么,在这种情况下可能是:一切;这样做可以避免你陷入一个热循环,一遍又一遍地解析同一个不完整的帧。例如,我的一个读取循环看起来像(简化):

while (true)
{
    var readResult = await input.ReadAsync();
    var buffer = readResult.Buffer;
    int handled = TryConsume(ref buffer); // note: this **changes** buffer, slicing (Slice)
    // data from the start; when TryConsume exits, it will contain everything
    // that is *left*, but we will have effectively examined all of it; we will
    // have consumed any complete frames that we can from it

    // advance the pipe
    input.AdvanceTo(buffer.Start, buffer.End);

    // exit if we aren't making progress, and nothing else will be forthcoming
    if (handled == 0 && readResult.IsCompleted)
    {
        break; // no more data, or trailing incomplete messages
    }
}

推荐阅读