How do you implement a reusable named pipe listener that runs asynchronously?

Mike Pateras picture Mike Pateras · Sep 27, 2010 · Viewed 8.4k times · Source

I can't find a good example of how to create a reusable named pipe listener that runs asynchronously. I can make a reusable listener:

NamedPipeServerStream pipeServer = new NamedPipeServerStream("MyPipe", PipeDirection.InOut);

    while (true)
    {
            pipeServer.WaitForConnection();

            StreamReader reader = new StreamReader(pipeServer);

            MessageBox.Show(reader.ReadLine());

            pipeServer.Disconnect();
    }

and I can make an asychronous listener:

NamedPipeServerStream pipeServer = new NamedPipeServerStream("MyPipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);

    pipeServer.BeginWaitForConnection((a) =>
    {
        pipeServer.EndWaitForConnection(a);

        StreamReader reader = new StreamReader(pipeServer);
        MessageBox.Show(reader.ReadLine());

    }, null);

But I can't seem to get both going. Is there a good example for this? I'm also concerned about partially sent messages, as I believe that is an issue with asynchronous communications like this.

Update: I'm a little closer.

pipeServer = new NamedPipeServerStream("MyPipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);

pipeServer.BeginWaitForConnection((a) =>
{
    pipeServer.EndWaitForConnection(a);

    StreamReader reader = new StreamReader(pipeServer);

    while (running)
    {
        String text = reader.ReadLine();

        if (String.IsNullOrEmpty(text) == false)
        {
            MessageBox.Show(text);
        }
    }

    MessageBox.Show("Done!");

}, null);

That will read successfully once, and will continue looping, with ReadLine returning an empty empty string after the initial successful read. So it's clearly not blocking, and is attempting to read again. The problem is if I send the same message a second time, it doesn't get picked up, and my pipe writer says it's receiving error 2316 (though I can't figure out what that means). I think I just need to do something similar to this where the pipe gets cleaned up each time, like the first code sample I listed, but I haven't gotten that to work yet.

Answer

Mike Pateras picture Mike Pateras · Sep 28, 2010

I think I've got it:

pipeServer = new NamedPipeServerStream("MyPipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);

Boolean connectedOrWaiting = false;

Byte[] buffer = new Byte[65535];

while (running)
{
    if (!connectedOrWaiting)
    {                   
        pipeServer.BeginWaitForConnection((a) => { pipeServer.EndWaitForConnection(a); }, null);

        connectedOrWaiting = true;
    }

    if (pipeServer.IsConnected)
    {
        Int32 count = pipeServer.Read(buffer, 0, 65535);

        if (count > 0)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            String message = encoding.GetString(buffer, 0, count);

            MessageBox.Show(message);
        }

        pipeServer.Disconnect();

        connectedOrWaiting = false;
    }
}

This will accept multiple message as they come, and will shut down as soon as running is set to false (in another thread, obviously). It appears to be what I need. Can someone verify that I'm not doing anything silly?