Beispiel für Named Pipes

130

Wie schreibe ich ein einfaches Minimum, das erforderlich ist, damit es funktioniert - Testanwendung, die die Verwendung von IPC / Named Pipes veranschaulicht?

Wie würde man beispielsweise eine Konsolenanwendung schreiben, in der Programm 1 "Hallo Welt" zu Programm 2 sagt und Programm 2 eine Nachricht empfängt und "Roger That" zu Programm 1 antwortet.

Jordan Trainor
quelle

Antworten:

165
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();
                }
            });
        }
    }
}
PFUND
quelle
1
@ JordanTrainor Sorry, es ist in .Net 4.5. Sie können verwendenThread.Sleep
LB
2
@ Gusdor Ich hätte einige Sync-Primiten verwenden können. Aber es wäre schwieriger zu lesen. Ich denke, es ist genug, um eine Idee über die Verwendung von NamedPipes
LB
2
Wenn Sie das Problem haben, dass die Pipe nach einem Lesevorgang
geschlossen wird
11
Wenn Sie .NET 4.5, verwenden , können Sie ersetzen Task.Factory.StartNewmitTask.Run .
Rudey
2
Müssen Sie reader/ entsorgen writer? Wenn ja, entsorgen Sie nur einen von ihnen? Ich habe noch nie ein Beispiel gesehen, bei dem beide an denselben Stream angehängt sind.
JoshVarty
20

Für jemanden, der IPC und Named Pipes noch nicht kennt, war das folgende NuGet-Paket eine große Hilfe.

GitHub: Named Pipe Wrapper für .NET 4.0

So verwenden Sie zuerst das Paket:

PS> Install-Package NamedPipeWrapper

Dann ein Beispielserver (vom Link kopiert):

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

Beispielclient:

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

client.Start();

Das Beste für mich ist, dass es im Gegensatz zu der hier akzeptierten Antwort mehrere Clients unterstützt, die mit einem einzelnen Server kommunizieren.

Martin Laukkanen
quelle
5
Ich würde dieses NuGet-Paket nicht für die Produktion empfehlen. Ich habe es implementiert und es hat einige Fehler, hauptsächlich weil ich nicht wirklich wissen kann, wann eine Nachricht am anderen Ende der Pipe vollständig empfangen wurde (führt zu unterbrochenen Verbindungen oder zu frühem Ende der Verbindung (überprüfen Sie den Code auf) github Wenn du mir nicht vertraust, wird "WaitForPipeDrain" nicht aufgerufen, wenn es sollte), und du hast mehrere Clients, auch wenn nur einer zuhört, weil ... zu viele Probleme). Es ist traurig, weil es wirklich einfach zu bedienen war. Ich musste eine mit weniger Optionen von Grund auf neu erstellen.
Micaël Félix
Ja, guter Punkt, leider hat der ursprüngliche Betreuer das Projekt seit Jahren nicht mehr aktualisiert, zum Glück gibt es eine Reihe von Gabeln, von denen die meisten die von Ihnen besprochenen Probleme beheben.
Martin Laukkanen
2
@MartinLaukkanen: Hallo, ich habe vor, NamedPipeWrapper zu verwenden. Weißt du, welche Gabel diesen Fehler behebt? danke
Whiletrue
16

Sie können tatsächlich mit dem Namen btw in eine Named Pipe schreiben.

Öffnen Sie eine Befehlsshell als Administrator, um den Standardfehler "Zugriff verweigert" zu umgehen:

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

FYI dotnet core unter Linux unterstützt keine Namedpipes. Versuchen Sie stattdessen tcplistener, wenn Sie unter Linux arbeiten

Dieser Code hat einen Client-Roundtrip pro Byte.

  • Client schreibt Byte
  • Server liest Byte
  • Server schreibt Byte
  • Client liest 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 <-- 
            });
        }
    }
}
Patrick
quelle
Die Verwendung von Named Pipes wie diesen für 2 Prozesse macht michSystem Unauthorized Accesss Exception - path is denied
Bercovici Adrian
Nicht sicher, ob Sie vielleicht als Administrator arbeiten?
Patrick