Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

C# Async Named Pipes

4.91/5 (48 votes)
11 Nov 2012CPOL3 min read 191.6K   10.9K  
How to use named pipes with Async
Image 1

Introduction

The following tip shows how to implement a Named Piped Client/Server using asynchronous methods for communications between .NET applications.

Background

I created this article because I found it hard to find good examples of Named Pipe Client/Server that use async methods. I found a great article on CodeProject by "Maddog Mike B" but it only worked on .NET 3.5, so I had to come up with a new version for .NET 4.5. Most Named Pipe examples seem to be based on synchronous pipes spliced with threading to create asynchronous Pipes. I don't know if the method I provide is correct but it's what has resulted after hours of scouring the net piecing together scraps of information.

Overview

This code was tested with Visual Studio 2012 and .NET 4.5. The project is divided into two winforms based projects, PipeTestClient (sends messages to the server) and PipeTestServer (listens for incoming messages from clients). Both projects have been supplied in zip format.

The AsyncCallback method is called when the method completes. In .NET, events can trigger delegates, but also methods can trigger delegates. The AsyncCallback class allows methods to start an asynchronous function and supply a delegate method to call when the asynchronous function completes.

The state object can be used to pass information between the asynchronous function call and the corresponding AsyncCallback method.

Async Listen Method [Listen Server Class]

The Listen() method is called taking one argument - PipeName, this is assigned to a class level var for use later in a recursive function. The NamedPipeServerStream is created using the PipeOptions.Asynchronous argument (needed for async operation). The passed in PipeName is also used. (Must be the same name as the Client). The Servers asynchronous BeginWaitFoConnection() method is called using AsyncCallback to trigger the WaitForConnectionCallback method, which also receives the state object - in this case the original Pipe. Once into the <span style="FONT-FAMILY: 'Segoe UI', Arial, sans-serif; COLOR: rgb(17,17,17); FONT-SIZE: 14px"></span><code>WaitForConnectionCallback method, the passed state pipe is allocated to a local var and waiting for the connection is ended. A read buffer is created and the message data is read into and converted to a string type. The Original server pipe is then killed and a new one created using the same criteria and the original, the new server pipe begins to wait for another connection using its own method as the AsyncCallback Function (recursion).

C#
// Delegate for passing received message back to caller
public delegate void DelegateMessage(string Reply);

class PipeServer
{
    public event DelegateMessage PipeMessage;
    string _pipeName;

    public void Listen(string PipeName)
    {
        try
        {
            // Set to class level var so we can re-use in the async callback method
            _pipeName = PipeName;
            // Create the new async pipe 
            NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeName, 
               PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

            // Wait for a connection
            pipeServer.BeginWaitForConnection
            (new AsyncCallback(WaitForConnectionCallBack), pipeServer);
        }
        catch (Exception oEX)
        {
            Debug.WriteLine(oEX.Message);
        }
    }

    private void WaitForConnectionCallBack(IAsyncResult iar)
    {
        try
        {
            // Get the pipe
            NamedPipeServerStream pipeServer = (NamedPipeServerStream)iar.AsyncState;
            // End waiting for the connection
            pipeServer.EndWaitForConnection(iar);

            byte[] buffer = new byte[255];

            // Read the incoming message
            pipeServer.Read(buffer, 0, 255);
            
            // Convert byte buffer to string
            string stringData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
            Debug.WriteLine(stringData + Environment.NewLine);

            // Pass message back to calling form
            PipeMessage.Invoke(stringData);

            // Kill original sever and create new wait server
            pipeServer.Close();
            pipeServer = null;
            pipeServer = new NamedPipeServerStream(_pipeName, PipeDirection.In, 
               1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);

            // Recursively wait for the connection again and again....
            pipeServer.BeginWaitForConnection(
               new AsyncCallback(WaitForConnectionCallBack), pipeServer);
        }
        catch
        {
            return;
        }
    }
}

Async Send Method [PipeClient Class]

The Send() method is called and takes Send String, Pipe name and connection timeout to the server in milliseconds as arguments. Moving through the code, a NamedPipedClientStream (Pipe) is created using the PipeOptions.Asynchronous argument, this is vital if we are going to succeed in sending an async pipe message. We also use the passed in PipeName (this should be the same name as the server is using). Once created, we try to connect to the server, if the timeout argument is left out and the server does not exist, then the Connect method will sit and wait indefinitely, so it makes sense to use a timeout (in milliseconds). A byte array is then created and the send string/message is converted to it. At this point, assuming a successful connection, the clients asynchronous BeginWrite() method is called using AsyncCallback to trigger the AsyncSend method, which also receives the state object - in this case the original Pipe. Once into the AsyncSend method, the passed state pipe is allocated to a local var and the write is ended.

C#
class PipeClient
{
    public void Send(string SendStr, string PipeName, int TimeOut = 1000)
    {
        try
        {
            NamedPipeClientStream pipeStream = new NamedPipeClientStream
               (".", PipeName, PipeDirection.Out, PipeOptions.Asynchronous);

            // The connect function will indefinitely wait for the pipe to become available
            // If that is not acceptable specify a maximum waiting time (in ms)
            pipeStream.Connect(TimeOut);
            Debug.WriteLine("[Client] Pipe connection established");

            byte[] _buffer = Encoding.UTF8.GetBytes(SendStr);
            pipeStream.BeginWrite
            (_buffer, 0, _buffer.Length, new AsyncCallback(AsyncSend), pipeStream);
        }
        catch (TimeoutException oEX)
        {
            Debug.WriteLine(oEX.Message);
        }
    }

    private void AsyncSend(IAsyncResult iar)
    {
        try
        {
            // Get the pipe
            NamedPipeClientStream pipeStream = (NamedPipeClientStream)iar.AsyncState;

            // End the write
            pipeStream.EndWrite(iar);
            pipeStream.Flush();
            pipeStream.Close();
            pipeStream.Dispose();
        }
        catch (Exception oEX)
        {
            Debug.WriteLine(oEX.Message);
        }
    }
}

Usage

The Server code also includes event delegates to message back to the calling form - included is a threadsafe method for cross thread communication - please see the code.

Start both the client and server apps, Click Listen on the server App. Then click send as many times as you like on the client app. The messaging is instant and reliable.

History

  • Version 1.0

Help

If anyone has any tips or tricks for making this cleaner/faster, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)