Exemplo de pipes nomeados

131

Como escrevo um aplicativo simples e mínimo necessário para que ele funcione - teste que ilustra como usar IPC / Pipes Nomeados?

Por exemplo, como alguém escreveria um aplicativo de console em que o Programa 1 diz "Olá Mundo" para o Programa 2 e o Programa 2 recebe uma mensagem e responde "Roger That" para o Programa 1.

Jordan Trainor
fonte

Respostas:

166
using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            StartServer();
            Task.Delay(1000).Wait();


            //Client
            var client = new NamedPipeClientStream("PipesOfPiece");
            client.Connect();
            StreamReader reader = new StreamReader(client);
            StreamWriter writer = new StreamWriter(client);

            while (true)
            {
                string input = Console.ReadLine();
                if (String.IsNullOrEmpty(input)) break;
                writer.WriteLine(input);
                writer.Flush();
                Console.WriteLine(reader.ReadLine());
            }
        }

        static void StartServer()
        {
            Task.Factory.StartNew(() =>
            {
                var server = new NamedPipeServerStream("PipesOfPiece");
                server.WaitForConnection();
                StreamReader reader = new StreamReader(server);
                StreamWriter writer = new StreamWriter(server);
                while (true)
                {
                    var line = reader.ReadLine();
                    writer.WriteLine(String.Join("", line.Reverse()));
                    writer.Flush();
                }
            });
        }
    }
}
LIBRA
fonte
1
@JordanTrainor Desculpe, está no .Net 4.5. Você pode usarThread.Sleep
LB
2
@Gusdor Eu poderia ter usado algumas primeiras de sincronização. Mas seria mais difícil de ler. Eu acho que é o suficiente para dar uma idéia sobre como usar NamedPipes
LB
2
Se você tem o problema que o fechamento do tubo depois de uma leitura, verifique esta resposta: stackoverflow.com/a/895656/941764
jgillich
11
Se você estiver usando o .NET 4.5, poderá substituir Task.Factory.StartNewporTask.Run .
Rudey 27/03
2
Você tem que descartar reader/ writer? Se sim, você apenas descarta um deles? Eu nunca vi um exemplo em que ambos estejam anexados ao mesmo fluxo.
JoshVarty 24/09/2015
21

Para alguém que é novo no IPC e nos Named Pipes, achei o pacote NuGet a seguir uma grande ajuda.

GitHub: Wrapper de tubulação nomeado para .NET 4.0

Para usar primeiro instale o pacote:

PS> Install-Package NamedPipeWrapper

Em seguida, um servidor de exemplo (copiado do link):

var server = new NamedPipeServer<SomeClass>("MyServerPipe");
server.ClientConnected += delegate(NamedPipeConnection<SomeClass> conn)
    {
        Console.WriteLine("Client {0} is now connected!", conn.Id);
        conn.PushMessage(new SomeClass { Text: "Welcome!" });
    };

server.ClientMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Client {0} says: {1}", conn.Id, message.Text);
    };

server.Start();

Cliente de exemplo:

var client = new NamedPipeClient<SomeClass>("MyServerPipe");
client.ServerMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Server says: {0}", message.Text);
    };

client.Start();

A melhor coisa para mim é que, diferentemente da resposta aceita aqui, ele suporta vários clientes conversando com um único servidor.

Martin Laukkanen
fonte
5
Eu não recomendaria este pacote NuGet para produção. Eu o implementei e ele tem alguns bugs, principalmente por não saber quando uma mensagem foi totalmente recebida na outra extremidade do canal (leva a conexões quebradas ou a conexão termina muito cedo) (verifique o código em github se você não confia em mim, "WaitForPipeDrain" não é chamado quando deveria), além de ter vários clientes, mesmo quando apenas um está ouvindo, porque ... muitos problemas). É triste porque foi realmente fácil de usar. Eu tive que reconstruir um do zero com menos opções.
Micaël Félix 27/09/16
Sim, bom ponto, infelizmente esse mantenedor original não atualiza o projeto há anos, felizmente, embora existam vários garfos, a maioria dos quais corrige os problemas que você discutiu.
Martin Laukkanen
2
@MartinLaukkanen: Olá, pretendo usar NamedPipeWrapper, você sabe qual fork está corrigindo esse bug? Obrigado #
Whiletrue
17

Na verdade, você pode gravar em um pipe nomeado usando seu nome, btw.

Abra um shell de comando como administrador para solucionar o erro padrão "Acesso negado":

echo Hello > \\.\pipe\PipeName
Michael Blankenship
fonte
3

FYI dotnet core no linux não suporta pipes nomeados, tente tcplistener se estiver no linux

Esse código tem um cliente de ida e volta de um byte.

  • Cliente escreve byte
  • O servidor lê byte
  • O servidor grava byte
  • Cliente lê byte

Servidor do DotNet Core 2.0

using System;
using System.IO.Pipes;
using System.Threading.Tasks;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            var server = new NamedPipeServerStream("A", PipeDirection.InOut);
            server.WaitForConnection();

            for (int i =0; i < 10000; i++)
            {
                var b = new byte[1];
                server.Read(b, 0, 1); 
                Console.WriteLine("Read Byte:" + b[0]);
                server.Write(b, 0, 1);
            }
        }
    }
}

Cliente do DotNet Core 2.0 ConsoleApp

using System;
using System.IO.Pipes;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        public static int threadcounter = 1;
        public static NamedPipeClientStream client;

        static void Main(string[] args)
        {
            client = new NamedPipeClientStream(".", "A", PipeDirection.InOut, PipeOptions.Asynchronous);
            client.Connect();

            var t1 = new System.Threading.Thread(StartSend);
            var t2 = new System.Threading.Thread(StartSend);

            t1.Start();
            t2.Start(); 
        }

        public static void StartSend()
        {
            int thisThread = threadcounter;
            threadcounter++;

            StartReadingAsync(client);

            for (int i = 0; i < 10000; i++)
            {
                var buf = new byte[1];
                buf[0] = (byte)i;
                client.WriteAsync(buf, 0, 1);

                Console.WriteLine($@"Thread{thisThread} Wrote: {buf[0]}");
            }
        }

        public static async Task StartReadingAsync(NamedPipeClientStream pipe)
        {
            var bufferLength = 1; 
            byte[] pBuffer = new byte[bufferLength];

            await pipe.ReadAsync(pBuffer, 0, bufferLength).ContinueWith(async c =>
            {
                Console.WriteLine($@"read data {pBuffer[0]}");
                await StartReadingAsync(pipe); // read the next data <-- 
            });
        }
    }
}
patrick
fonte
Usando pipes nomeados como esta para 2 processos me tornaSystem Unauthorized Accesss Exception - path is denied
Bercovici Adrian
Não tem certeza de que talvez seja executado como administrador?
patrick