Click here to Skip to main content
Click here to Skip to main content

C# Async Named Pipes

, 11 Nov 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How to use named pipes with Async

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 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).

// 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.

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)

Share

About the Author

Nemo1966

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberMember 1079835230-Jul-14 23:39 
QuestionSupport for multiple servers PinmemberAnalogKid1726-Jun-14 11:48 
QuestionMake it two ways communication Pinmemberrezer12-Mar-14 18:42 
AnswerRe: Make it two ways communication Pinmemberjohn_172628-Apr-14 9:51 
SuggestionMaking a background worker for a web application that uses named pipe to communicate. PinmemberMember 771788630-Nov-13 21:27 
GeneralMy vote of 5 PinmemberChrDressler28-Sep-13 6:01 
GeneralMy vote of 5 PinmemberThierry Bachmann2-Sep-13 5:18 
GeneralThanks PinmemberMember 893364124-Aug-13 16:52 
GeneralMy vote of 5 PinprofessionalEdo Tzumer22-Jun-13 23:01 
GeneralMy vote of 5 PinmemberJerryShiu28-Apr-13 23:12 
QuestionPipeServer PinmemberBuratino2k28-Feb-13 20:54 
Questionasync await? [modified] Pinmemberchadbr28-Nov-12 8:56 
Thanks Nemo - this is just what I was looking for -- I've made some "improvements" using streams and async / await. Greatly simplifies the code.
 
Drop me an e-mail and I'll send you the whole project if you'd like.
 
Client side:
 
 

private async void cmdSend_Click(object sender, EventArgs e)
{
    cmdSend.Enabled = false;
    await _pipeClient.SendAwait(txtMessage.Text + " - " + _ctr.ToString(), "TestPipe", 1000);
    cmdSend.Enabled = true;
    _ctr++;
}
 
public async Task SendAwait(string SendStr, string PipeName, int TimeOut = 1000)
{
    using (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");
 
        using (StreamWriter sw = new StreamWriter(pipeStream))
        {
            await sw.WriteAsync(SendStr);
 
            // flush
            await pipeStream.FlushAsync();
        }
    }
}
 
Server side:
 
private async void cmdListen_Click(object sender, EventArgs e)
{
    txtMessage.Text = "Listening - OK";
    cmdListen.Enabled = false;
    while (true)
    {
 
        string message = await AsyncPipeServer.Listen("TestPipe");
        txtMessage.Text = message;
    }
}
 

class AsyncPipeServer
{
    public static async Task<string> Listen(string PipeName)
    {
        using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous))
        {
 
            await Task.Factory.FromAsync(pipeServer.BeginWaitForConnection, pipeServer.EndWaitForConnection, null);
 
            using (StreamReader reader = new StreamReader(pipeServer))
            {
                string text = await reader.ReadToEndAsync();
 
                return text;
            }
        }
    }
}
 
Unfortunately there is no magic by me here -- all things picked up via conferences and google searches.
 

I've known about async / await for a long time - but I didn't really "grock" it until I sat in this session at Build 2012:
Easy Asynchrony with C#: No More Callbacks! - Mads Torgersen
http://channel9.msdn.com/Events/Build/2012/3-011
 
Turn IAsyncResult code into the new async and await Keyword Pattern
http://www.justjuzzy.com/2012/10/turn-iasyncresult-code-into-await-keyword/
 

Stream on Pipe tip:
http://stackoverflow.com/questions/12047430/namedpipeserverstream-and-the-await-keyword

modified 4-Dec-12 8:54am.

AnswerRe: async await? PinmemberRoger50028-May-13 23:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.141022.2 | Last Updated 11 Nov 2012
Article Copyright 2012 by Nemo1966
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid