通过.NET中的PipeReader读取SSL

时间:2019-07-13 11:37:25

标签: c# .net asp.net-core .net-core io

我正在尝试在.NET中创建客户端库。

目前,我通过使用SSL流(包装在缓冲流中)并仅使用字节数组对流进行读/写来实现有效的实现。

我想让它更快,从一些阅读看来,System.IO.Pipelines是实现高性能IO的方法。

我只阅读了许多文章/演示,直接使用套接字演示了代码-自从我使用SSL以来,这似乎对我不起作用。

我找到了一些扩展来从流Stream.UsePipeReader()Stream.UsePipeWriter()中获取管道读取器/写入器,所以我尝试调用 SSLStream.UsePipeReader()

但是我始终收到错误消息:

System.NotSupportedException :  The ReadAsync method cannot be called when another read operation is pending.
   at System.Net.Security.SslStreamInternal.ReadAsyncInternal[TReadAdapter](TReadAdapter adapter, Memory`1 buffer)
   at Nerdbank.Streams.PipeExtensions.<>c__DisplayClass5_0.<<UsePipeReader>b__1>d.MoveNext() in D:\a\1\s\src\Nerdbank.Streams\PipeExtensions.cs:line 92
--- End of stack trace from previous location where exception was thrown ---
   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()
   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
   at System.IO.Pipelines.Pipe.GetReadAsyncResult()

我要从管道读取的代码是:

private async Task<string> ReadAsync(PipeReader reader)
        {

            var stringBuilder = new StringBuilder();

            while (true)
            {
                var result = await reader.ReadAsync();

                var buffer = result.Buffer;
                SequencePosition? position;

                do
                {
                    // Look for a EOL in the buffer
                    position = buffer.PositionOf((byte) '\n');

                    if (position != null)
                    {
                        // Process the line
                        ProcessLine(buffer.Slice(0, position.Value), stringBuilder);
                        buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
                    }
                } while (position != null);

                reader.AdvanceTo(buffer.Start, buffer.End);

                if (result.IsCompleted)
                {
                    reader.Complete();
                    break;
                }
            }

            return stringBuilder.ToString();
        }

它没有被其他任何线程调用,因为我已经对其进行了锁定测试。为了测试目的,我一次只打一个电话。

有人可以告诉我我在做什么错吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

我相信我可能知道答案。

您的问题可能在此行:

var result = await reader.ReadAsync();

您看到的是,等待仅在Send方法中阻止执行,但是与此同时,即使ReadAsync()尚未完成,调用方中的代码仍继续执行。

现在,如果在ReadAsync()完成之前再次调用Send方法,则将收到已发布的异常,因为SslStream不允许进行多个读/写操作,并且已发布的代码也不会阻止这种情况从发生。

在这种情况下,您需要编写自己的安全失败机制,以避免在完成第一个调用之前进行第二个调用。

由于您在此处使用循环:

while (true)
{
  //rest of code...
}

我相信这是您应该解决问题的地方。 您正在到达第二个迭代而第一个尚未完成,因此导致:

System.NotSupportedException

答案 1 :(得分:0)

您不能在另一个readasync中执行readasync,我认为您从pipereader派生的方法名称已更改为其他名称。

private async Task<string> **ReadAsync**(PipeReader reader)
        {

            var stringBuilder = new StringBuilder();

            while (true)
            {
                var result = await reader.**ReadAsync**();

尝试这样修改。

**

async ValueTask ReadSomeDataAsync(PipeReader reader)
{
    while (true)
    {
        // await some data being available
        ReadResult read = await reader.ReadAsync();
        ReadOnlySequence<byte> buffer = read.Buffer;
        // check whether we've reached the end
        // and processed everything
        if (buffer.IsEmpty && read.IsCompleted)
            break; // exit loop
        // process what we received
        foreach (Memory<byte> segment in buffer)
        {
            string s = Encoding.ASCII.GetString(
                segment.Span);
            Console.Write(s);
        }
        // tell the pipe that we used everything
        reader.AdvanceTo(buffer.End);
    }
}

**

您仍然可以通过任何方式进行内存流传输。