155

How do I write a simple--bare minimum needed for it to work--test application that illustrates how to use IPC/Named Pipes?

For example, how would one write a console application where Program 1 says "Hello World" to Program 2 and Program 2 receives message and replies "Roger That" to Program 1.

Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Jordan Trainor
  • 2,410
  • 5
  • 21
  • 23

4 Answers4

196
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();
                }
            });
        }
    }
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • Just wondering what Line: 16 is supposed to be because there's no definition for 'Delay' in System.Thread.Tasks – Jordan Trainor Dec 10 '12 at 18:37
  • 1
    @JordanTrainor Sorry, it is in .Net 4.5. You can use `Thread.Sleep` – L.B Dec 10 '12 at 18:42
  • 1
    The use of Task in this example is pretty dire. It is verbose syntax and due to scheduling may not being even close to 1000ms. – Gusdor Aug 22 '13 at 10:08
  • 3
    @Gusdor I could have used some sync-primiteves. But It would be more hard to read. I think it is enough to give an idea about how to use NamedPipes – L.B Aug 22 '13 at 13:06
  • 2
    If you have the problem that the pipe closes after one read, check this answer: http://stackoverflow.com/a/895656/941764 – jgillich Nov 11 '13 at 10:41
  • 1
    Thanks a bunch mate! P.s. the server should have (!reader.EndOfStream) instead of while(true). – joonas.fi Mar 20 '15 at 22:05
  • 14
    If you're using .NET 4.5, you can [replace `Task.Factory.StartNew` with `Task.Run`](http://stackoverflow.com/a/22087211/1185136). – Rudey Mar 27 '15 at 13:30
  • 2
    Do you have to dispose of `reader`/`writer`? If so, do you only dispose of one of them? I've never seen an example where both are attached to the same stream. – JoshVarty Sep 24 '15 at 00:58
27

For someone who is new to IPC and Named Pipes, I found the following NuGet package to be a great help.

GitHub: Named Pipe Wrapper for .NET 4.0

To use first install the package:

PS> Install-Package NamedPipeWrapper

Then an example server (copied from the 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();

Example client:

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

client.Start();

Best thing about it for me is that unlike the accepted answer here it supports multiple clients talking to a single server.

  • 11
    I wouldn't recommend this NuGet package for production. I've implemented it and it has some bugs, mainly due to not being able to really know when a message has been fully received in the other end of the pipe (leads to broken connections, or connection ending too soon (check the code on github if you don't trust me, "WaitForPipeDrain" isn't called when it should), plus you'll have multiple clients even when only one is listening because... too many issues). It's sad because it was really easy to use. I had to rebuild one from scratch with less options. – Micaël Félix Sep 27 '16 at 10:46
  • Yes good point, unfortunately that original maintainer has not updated the project for years, fortunately though a number of forks exist most of which fix the issues you discussed. – Martin Laukkanen Nov 08 '16 at 16:08
  • 2
    @MartinLaukkanen : Hello, I plan to use NamedPipeWrapper, You you know which fork is fixing this bug ? thanks – Whiletrue Nov 15 '18 at 15:45
  • @MartinLaukkanen Can we have the fork that fixed the bugs mentioned? – Ibanez1408 Aug 17 '20 at 07:19
  • I don't recall which one I used specifically, but I would suggest reviewing the commits on the fork network graph to determine which one fixes the issues you are concerned with: https://github.com/acdvorak/named-pipe-wrapper/network – Martin Laukkanen Aug 21 '20 at 08:08
  • I'd also avoid this package. There is a potential security issue because it uses BinaryFormatter. I refactored it heavily but by the end there was nothing much of the original left. – Tony Edgecombe Dec 16 '21 at 10:18
23

You can actually write to a named pipe using its name, btw.

Open a command shell as Administrator to get around the default "Access is denied" error:

echo Hello > \\.\pipe\PipeName
Michael Blankenship
  • 1,639
  • 10
  • 16
4

Linux dotnet core doesn't support namedpipes!

Try TcpListener if you deploy to Linux

This NamedPipe Client/Server code round trips a byte to a server.

  • Client writes byte
  • Server reads byte
  • Server writes byte
  • Client reads 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
  • 16,091
  • 29
  • 100
  • 164