Exemple de tuyaux nommés

131

Comment écrire une application de test simple - le strict minimum nécessaire pour que cela fonctionne - qui illustre comment utiliser IPC / Named Pipes?

Par exemple, comment écrire une application console où le programme 1 dit «Hello World» au programme 2 et le programme 2 reçoit un message et répond «Roger That» au programme 1.

Jordan Trainor
la source

Réponses:

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();
                }
            });
        }
    }
}
KG
la source
1
@JordanTrainor Désolé, c'est dans .Net 4.5. Vous pouvez utiliserThread.Sleep
LB
2
@Gusdor J'aurais pu utiliser des primites de synchronisation. Mais ce serait plus difficile à lire. Je pense qu'il suffit de donner une idée sur la façon d'utiliser NamedPipes
LB
2
Si vous rencontrez le problème que le tuyau se ferme après une lecture, vérifiez cette réponse: stackoverflow.com/a/895656/941764
jgillich
11
Si vous utilisez .NET 4.5, vous pouvez le remplacer Task.Factory.StartNewparTask.Run .
Rudey
2
Devez-vous disposer de reader/ writer? Si oui, en disposez-vous uniquement? Je n'ai jamais vu d'exemple où les deux sont attachés au même flux.
JoshVarty
21

Pour quelqu'un qui est nouveau dans IPC et Named Pipes, j'ai trouvé que le package NuGet suivant était d'une grande aide.

GitHub: wrapper de pipe nommé pour .NET 4.0

Pour utiliser d'abord installer le package:

PS> Install-Package NamedPipeWrapper

Puis un exemple de serveur (copié à partir du lien):

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

Exemple de client:

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

client.Start();

La meilleure chose à ce sujet pour moi est que contrairement à la réponse acceptée ici, il prend en charge plusieurs clients qui parlent à un seul serveur.

Martin Laukkanen
la source
5
Je ne recommanderais pas ce package NuGet pour la production. Je l'ai implémenté et il a quelques bugs, principalement dus au fait de ne pas pouvoir vraiment savoir quand un message a été entièrement reçu à l'autre bout du tuyau (conduit à des connexions rompues, ou à une connexion se terminant trop tôt (vérifiez le code sur github si vous ne me faites pas confiance, "WaitForPipeDrain" n'est pas appelé quand il le devrait), en plus vous aurez plusieurs clients même si un seul écoute parce que ... trop de problèmes). C'est triste car c'était vraiment facile à utiliser. J'ai dû en reconstruire un à partir de zéro avec moins d'options.
Micaël Félix
Oui, bon point, malheureusement, le mainteneur d'origine n'a pas mis à jour le projet depuis des années, heureusement s'il existe un certain nombre de fourchettes dont la plupart résolvent les problèmes dont vous avez discuté.
Martin Laukkanen
2
@MartinLaukkanen: Bonjour, je prévois d'utiliser NamedPipeWrapper, vous savez quel fork corrige ce bug? merci
Whiletrue
17

Vous pouvez en fait écrire dans un tube nommé en utilisant son nom, btw.

Ouvrez un shell de commande en tant qu'administrateur pour contourner l'erreur par défaut «Accès refusé»:

echo Hello > \\.\pipe\PipeName
Michael Blankenship
la source
3

FYI dotnet core sur Linux ne prend pas en charge les namedpipes, essayez plutôt tcplistener si vous êtes sous Linux

Ce code a un client aller-retour d'un octet.

  • Le client écrit l'octet
  • Le serveur lit l'octet
  • Le serveur écrit l'octet
  • Le client lit l'octet

ConsoleApp du serveur 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);
            }
        }
    }
}

Application de console client DotNet Core 2.0

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
la source
Utiliser des tubes nommés comme celui-ci pour 2 processus me rendSystem Unauthorized Accesss Exception - path is denied
Bercovici Adrian
Vous n'êtes pas sûr de pouvoir exécuter en tant qu'administrateur?
patrick