TLS/SSL with System.IO.Pipelines



我注意到了新的System.IO.Pipelines,并试图将现有的、基于流的代码移植到它上面。流的问题很容易理解,但同时它具有丰富的相关类回声系统。

从这里提供的示例来看,有一个小型的tcp echo服务器。https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/

这里附上了一段代码:

private static async Task ProcessLinesAsync(Socket socket)
{
Console.WriteLine($"[{socket.RemoteEndPoint}]: connected");
var pipe = new Pipe();
Task writing = FillPipeAsync(socket, pipe.Writer);
Task reading = ReadPipeAsync(socket, pipe.Reader);
await Task.WhenAll(reading, writing);
Console.WriteLine($"[{socket.RemoteEndPoint}]: disconnected");
}
private static async Task FillPipeAsync(Socket socket, PipeWriter writer)
{
const int minimumBufferSize = 512;
while (true)
{
try
{
// Request a minimum of 512 bytes from the PipeWriter
Memory<byte> memory = writer.GetMemory(minimumBufferSize);
int bytesRead = await socket.ReceiveAsync(memory, SocketFlags.None);
if (bytesRead == 0)
{
break;
}
// Tell the PipeWriter how much was read
writer.Advance(bytesRead);
}
catch
{
break;
}
// Make the data available to the PipeReader
FlushResult result = await writer.FlushAsync();
if (result.IsCompleted)
{
break;
}
}
// Signal to the reader that we're done writing
writer.Complete();
}
private static async Task ReadPipeAsync(Socket socket, PipeReader reader)
{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;
SequencePosition? position = null;
do
{
// Find the EOL
position = buffer.PositionOf((byte)'n');
if (position != null)
{
var line = buffer.Slice(0, position.Value);
ProcessLine(socket, line);
// This is equivalent to position + 1
var next = buffer.GetPosition(1, position.Value);
// Skip what we've already processed including n
buffer = buffer.Slice(next);
}
}
while (position != null);
// We sliced the buffer until no more data could be processed
// Tell the PipeReader how much we consumed and how much we left to process
reader.AdvanceTo(buffer.Start, buffer.End);
if (result.IsCompleted)
{
break;
}
}
reader.Complete();
}
private static void ProcessLine(Socket socket, in ReadOnlySequence<byte> buffer)
{
if (_echo)
{
Console.Write($"[{socket.RemoteEndPoint}]: ");
foreach (var segment in buffer)
{
Console.Write(Encoding.UTF8.GetString(segment.Span));
}
Console.WriteLine();
}
}

使用流时,只需将SSL/TLS封装在SslStream中,就可以轻松地将其添加到代码中。管道如何解决这一问题?

命名管道是一种网络协议,就像HTTP、FTP和SMTP一样。让我们看看.net框架中的一些快速示例:

  • SSL被HTTP连接自动利用,取决于基本URI。如果URI带有"HTTPS:",则SSL为已使用
  • FTP连接手动利用SSL,方法是设置在调用GetResponse((之前,将EnableSsl属性设置为true
  • SMTP利用SSL的方式与FTP相同

但是如果我们使用不同的网络协议,比如管道,该怎么办?一开始我们就知道没有什么能比得上"HTTPS"前缀。此外,我们可以阅读System.IO.Principles的文档,发现没有"EnableSsl"方法。但是,在.NET Framework和.NET Core中,SslStream类都是可用的。这个类允许您从几乎所有可用的Stream中构建SslStream。

System.IO.Pipes命名空间在.NET Framework和.NET Core中都可用。Pipes命名空间中可用的类非常有用。

  • 匿名管道客户端流
  • 匿名管道服务器流
  • NamedPipeClientStream
  • 命名管道服务器流
  • PipeStream

所有这些类都返回某种从Stream继承的对象,因此可以在SslStream的构造函数中使用。

这与System.IO.p规程命名空间有何关联?好事实并非如此。System.IO.Pipelines命名空间中定义的类、结构或接口均未从Stream继承。因此,我们不能直接使用SslStream类。

相反,我们可以访问PipeReaders和PipeWriters。有时我们只有其中一个可用,但让我们考虑一个双向管道,这样我们就可以同时访问这两个管道。

System.IO.p规程命名空间有助于提供IDuplexPipe接口。如果我们想将PipeReader和PipeWriters封装在SSL流中,我们将需要定义一个实现IDuplexPipe的新类型。

在这种新型中:

  • 我们将定义一个SslStream
  • 我们将使用通用管道作为输入和输出缓冲区
  • PipeReader将使用输入缓冲区的读取器。我们将使用这个输入缓冲区从SSL流中获取数据
  • PipeWriter将使用输出缓冲区的写入程序。我们将使用这个输出缓冲区将数据发送到SSL流

以下是伪代码中的一个示例:

SslStreamDuplexPipe : IDuplexPipe
{ 
SslStream sslStream;
Pipe inputBuffer;
Pipe outputBuffer;
public PipeReader Input = inputBuffer.Reader;
public PipeWriter Output = outputBuffer.Writer;
ReadDataFromSslStream()
{
int bytes = sslStream.Read(new byte[2048], 0, 2048);
inputBuffer.Writer.Advance(bytes)
inputBuffer.Writer.Flush();
}
//and the reverse to write to the SslStream 
}

正如您所看到的,我们仍在使用System.Net.Security命名空间中的SslStream类,这只需要我们多走几步。

这是否意味着您基本上仍在使用流?是的!但是,一旦您完全实现了SslStreamDuplexPipe类,您就只能使用管道了。无需将SslStream包裹在所有内容上。

马克·格雷维尔对此写了一篇非常详细的解释。三个部分中的第一部分可以在这里找到:https://blog.marcgravell.com/2018/07/pipe-dreams-part-1.html

此外,您还可以阅读到提到的各种.NET类:

  • https://learn.microsoft.com/en-us/dotnet/api/system.io.pipelines.pipe?view=dotnet-平台-ext-2.1
  • https://learn.microsoft.com/en-us/dotnet/framework/network-programming/using-secure-sockets-layer
  • https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslstream?view=netcore-2.2
  • https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes?view=netcore-2.2

最新更新