Ejemplo de tuberías con nombre

131

¿Cómo escribo una aplicación de prueba simple, mínima necesaria para que funcione, que ilustra cómo usar IPC / Canalizaciones con nombre?

Por ejemplo, ¿cómo escribiría una aplicación de consola donde el Programa 1 dice "Hola mundo" al Programa 2 y el Programa 2 recibe un mensaje y responde "Roger That" al Programa 1.

Jordan Trainor
fuente

Respuestas:

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();
                }
            });
        }
    }
}
LB
fuente
1
@JordanTrainor Lo siento, está en .Net 4.5. Puedes usarThread.Sleep
LB
2
@ Gusdor, podría haber usado algunos sincronizadores primitivos. Pero sería más difícil de leer. Creo que es suficiente para dar una idea sobre cómo usar NamedPipes
LB
2
Si tiene el problema de que la tubería se cierra después de una lectura, verifique esta respuesta: stackoverflow.com/a/895656/941764
jgillich
11
Si está utilizando .NET 4.5, puede reemplazarlo Task.Factory.StartNewporTask.Run .
Rudey
2
¿Tienes que deshacerte de reader/ writer? Si es así, ¿solo dispone de uno de ellos? Nunca he visto un ejemplo en el que ambos estén conectados a la misma secuencia.
JoshVarty
21

Para alguien que es nuevo en IPC y Tuberías con nombre, el siguiente paquete NuGet me pareció de gran ayuda.

GitHub: Envoltorio de tubería con nombre para .NET 4.0

Para usar primero instale el paquete:

PS> Install-Package NamedPipeWrapper

Luego, un servidor de ejemplo (copiado del enlace):

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 ejemplo:

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

client.Start();

Lo mejor para mí es que, a diferencia de la respuesta aceptada aquí, es compatible con varios clientes que hablan con un solo servidor.

Martin Laukkanen
fuente
55
No recomendaría este paquete NuGet para producción. Lo he implementado y tiene algunos errores, principalmente debido a que no puedo saber realmente cuándo un mensaje se ha recibido completamente en el otro extremo de la tubería (conduce a conexiones interrumpidas o la conexión finaliza demasiado pronto (verifique el código en github si no confías en mí, "WaitForPipeDrain" no se llama cuando debería), además tendrás varios clientes incluso cuando solo uno esté escuchando porque ... demasiados problemas). Es triste porque fue realmente fácil de usar. Tuve que reconstruir uno desde cero con menos opciones.
Micaël Félix
Sí, buen punto, desafortunadamente el responsable original no ha actualizado el proyecto durante años, afortunadamente, aunque existen varios tenedores, la mayoría de los cuales solucionan los problemas que discutió.
Martin Laukkanen
2
@MartinLaukkanen: Hola, planeo usar NamedPipeWrapper, ¿Sabes qué bifurcación está arreglando este error? gracias
Since
17

En realidad, puede escribir en una tubería con nombre utilizando su nombre, por cierto.

Abra un shell de comandos como Administrador para evitar el error predeterminado "Acceso denegado":

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

Para su información, dotnet core en linux no es compatible con namedpipes, pruebe tcplistener en su lugar si está en linux

Este código tiene un cliente ida y vuelta por byte.

  • El cliente escribe byte
  • El servidor lee el byte
  • El servidor escribe byte
  • El cliente lee el byte

DotNet Core 2.0 Server ConsoleApp

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);
            }
        }
    }
}

DotNet Core 2.0 Client 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 <-- 
            });
        }
    }
}
Patricio
fuente
El uso de tuberías con nombre como este para 2 procesos me hace System Unauthorized Accesss Exception - path is denied
Bercovici Adrian
¿No está seguro de que tal vez se ejecute como administrador?
patrick