C# - Realizando a comunicação com Pipes - I


Hoje vamos tratar da comunicação entre processos usando Pipes (ou canais em uma tradução literal) na implementação feita na plataforma .NET.

Pipes , ou canais, numa tradução literal, são usados ​​para comunicação entre processos e threads. Normalmente, há um servidor de pipe único ao qual um ou mais clientes podem se conectar e trocar mensagens.

Na plataforma .NET os Pipes são implementados como streams e assim você tem uma opção para não somente enviar bytes em um pipe, mas você pode usar todos os recursos dos streams como os leitores (readers) e os escritores (writers).

Existem pipes nomeados e anônimos. Os pipes anônimos vêm com algumas limitações em comparação com os pipes nomeados:

Pipes são representados pelo namespace System.IO.Pipes na plataforma .NET. Estes são os principais objetos que você precisará, dependendo do tipo de canal com o qual deseja trabalhar:

Vejamos isso funcionando na prática.

Vamos começar com pipes nomeados para comunicação entre diferentes processos. Vamos criar dois aplicativos console onde um vai atuar como servidor lendo dados de um pipe e outro vai escrever mensagens para o pipe.

Vamos criar um projeto Console  App (.NET Core) usando o VS 2017 Community com o nome NetCore_Pipes;

1- Criando o Pipe servidor

A seguir inclua no arquivo Program o código abaixo:

using System;
using System.IO.Pipes;
using System.Text;
using static System.Console;

namespace NetCore_Pipes
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteLine("Usando Pipes");
            string nomePipe = args.Length == 1 ? args[0] : "PipeReaderDemo";
            PipesLeitor(nomePipe);
        }

        private static void PipesLeitor(string nomePipe)
        {
           WriteLine($"###  Servidor - {nomePipe}  ####");
            try
            {
                using (var pipeReader = new NamedPipeServerStream(nomePipe, PipeDirection.In))
                {
                    pipeReader.WaitForConnection();
                    WriteLine("leitor conectado");
                    const int BUFFERSIZE = 256;
                    bool terminado = false;
                    while (!terminado)
                    {
                        byte[] buffer = new byte[BUFFERSIZE];
                        int nRead = pipeReader.Read(buffer, 0, BUFFERSIZE);
                        string line = Encoding.UTF8.GetString(buffer, 0, nRead);
                        WriteLine(line);
                        if (line == "tchau") terminado = true;
                    }
                }
                WriteLine("leitura completada");
                ReadLine();
            }
            catch (Exception ex)
            {
                WriteLine(ex.Message);
            }
        }
    }
}

Vamos entender o código...

Criamos um servidor usando uma nova instância de NamedPipeServerStream que deriva da classe base PipeStream que, por sua vez, deriva da classe base Stream e, portanto, pode usar todos os recursos de streams , como criar um CryptoStream ou um GZipStream para gravar dados criptografados ou compactados no pipe nomeado.

O construtor requer um nome para o pipe que pode ser usado por vários processos que se comunicam por meio do pipe. O segundo argumento usado no trecho de código a seguir define a direção do pipe.

O servidor de stream é usado para leitura e, portanto, a direção é definida como PipeDirection.In. Os pipes nomeados podem também ser bidirecionais para leitura e escrita; neste caso usamos PipeDirection.InOut, já os Pipes anônimos podem ser somente unidirecional.

Em seguida, o pipe nomeado aguarda até que o participante se conecte chamando o Método WaitForConnection, depois dentro de um loop (até que a mensagem "tchau" seja recebida), o servidor do pipe lê mensagens para uma array de buffer e grava a mensagem no console:

2- Criando o Pipe cliente

Agora vamos criar o pipe cliente para gravar mensagens que serão lidas pelo servidor que já criamos.

Para isso vamos criar outro projeto Console  App (.NET Core) usando o VS 2017 Community com o nome NetCore_PipesCliente;

Inclua o código abaixo no arquivo Program:

using System;
using System.IO.Pipes;
using System.Text;
using static System.Console;

namespace NetCore_PipesCliente
{
    class Program
    {
        static void Main(string[] args)
        {
            string nomePipe = args.Length >= 1 ? args[0] : "ServidorPipe";
            string nomeServidor = args.Length >= 2 ? args[1] : "localhost";
            PipesWriter(nomeServidor , nomePipe);
        }

        private static void PipesWriter(string nomeServidor , string nomePipe)
        {
            WriteLine($"### Servidor - {nomePipe} ####");
            try
            {
                using (var pipeWriter = new NamedPipeClientStream(nomeServidor , nomePipe, PipeDirection.Out))
                {
                    pipeWriter.Connect();
                    WriteLine("Escritor(writer) conectado !!");

                    bool completed = false;
                    while (!completed)
                    {
                        string input = ReadLine();
                        if (input == "tchau") completed = true;

                        byte[] buffer = Encoding.UTF8.GetBytes(input);
                        pipeWriter.Write(buffer, 0, buffer.Length);
                    }
                }
                WriteLine("Escrita terminada...");
            }
            catch (Exception ex)
            {
                WriteLine(ex.Message);
            }
        }
    }
}

Criamos um cliente  instanciando um objeto NamedPipeClientStream.

Como os pipes nomeados podem se comunicar através da rede, você precisa de um nome de servidor, além do nome do pipe e da direção do pipe.

O cliente se conecta invocando o método Connect. Depois que a conexão for bem-sucedida, mensagens
são enviados para o servidor invocando WriteLine no StreamWriter.

Por padrão, as mensagens não são enviadas imediatamente; eles estão em cache. A mensagem é enviada para o servidor, invocando o método Flush. Você também pode passar imediatamente todas as mensagens sem invocar o método Flush. Para isso, você tem que configure a opção para gravar através do cache ao criar o pipe.

Executando primeiro o programa do Pipe servidor e a seguir do Pipe cliente iremos obter o seguinte resultado:

Tudo o que você digita no cliente ecoa no servidor...

Na próxima parte do artigo veremos como criar pipes anônimos.

Pegue o código dos exemplos aqui :  NetCore_Pipes.zip  e  NetCore_PipesCliente.zip

"Porque onde há inveja e espírito faccioso aí há perturbação e toda a obra perversa.
Mas a sabedoria que do alto vem é, primeiramente pura, depois pacífica, moderada, tratável, cheia de misericórdia e de bons frutos, sem parcialidade, e sem hipocrisia."

Tiago 3:16,17

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti