J'ai remarqué le nouveau System.IO.Pipelines et j'essaie de lui porter du code existant, basé sur le flux. Les problèmes liés aux flux sont bien compris, mais il comporte en même temps un riche écosystème de classes apparentées.
Dans l'exemple présenté ici, il existe un petit serveur d'écho tcp . https://blogs.msdn.Microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io -in-net/
Un extrait du code est attaché ici:
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();
}
}
Lorsque vous utilisez des flux, vous pouvez facilement ajouter SSL/TLS à votre code simplement en l’enveloppant dans SslStream. Comment cela est-il censé être résolu avec Pipelines?
Les canaux nommés sont un protocole réseau, tout comme HTTP, FTP et SMTP. Regardons le .net Framework pour quelques exemples rapides:
Mais que se passe-t-il si nous utilisons un protocole réseau différent, tel que des canaux? Dès le départ, nous savons que rien ne ressemble à un préfixe "HTTPS". De plus, nous pouvons lire la documentation de System.IO.Piplines et constater qu’il n’existe pas de méthode "EnableSsl". Toutefois, dans .NET Framework et .NET Core, la classe SslStream est disponible. Cette classe vous permet de créer un flux SslStream à partir de presque tous les flux disponibles.
L'espace de noms System.IO.Pipes est également disponible dans .NET Framework et .NET Core. Les classes disponibles dans l'espace de noms Pipes sont très utiles.
Toutes ces classes renvoient une sorte d'objet qui hérite de Stream et peut donc être utilisé dans le constructeur d'un SslStream.
Comment cela est-il lié à l'espace de noms System.IO.Piplines? Eh bien ... ça ne va pas. Aucune des classes, structures ou interfaces définies dans l'espace de noms System.IO.Pipelines n'hérite de Stream. Nous ne pouvons donc pas utiliser directement la classe SslStream.
Au lieu de cela, nous avons accès à PipeReaders et PipeWriters. Parfois, nous n'en avons qu'un, mais considérons un tuyau bidirectionnel pour avoir accès aux deux en même temps.
L'espace de noms System.IO.Piplines fournit utilement une interface IDuplexPipe. Si nous voulons envelopper PipeReader et PipeWriters dans un flux SSL, nous devons définir un nouveau type qui implémente IDuplexPipe.
Dans ce nouveau type:
Voici un exemple en pseudocode:
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
}
Comme vous pouvez le constater, nous utilisons toujours la classe SslStream de l’espace de noms System.Net.Security. Cela nous a pris quelques étapes supplémentaires.
Cela signifie-t-il que vous utilisez toujours les flux? Oui! Cependant, une fois que vous avez entièrement implémenté votre classe SslStreamDuplexPipe, vous ne pouvez utiliser que des tubes. Pas besoin d'enrouler un SslStream autour de tout.
Marc Gravell a écrit une explication beaucoup plus détaillée à ce sujet. La première des 3 parties peut être trouvée ici: https://blog.marcgravell.com/2018/07/pipe-dreams-part-1.html
De plus, vous pouvez en savoir plus sur les différentes classes .NET mentionnées: