Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#
Tip/Trick

C# Async Named Pipes

Rate me:
Please Sign up or sign in to vote.
4.91/5 (47 votes)
11 Nov 2012CPOL3 min read 186K   10.8K   71   26
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)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMessage Closed Pin
11-Nov-20 13:19
Member 1499050811-Nov-20 13:19 
QuestionMessage Closed Pin
28-Oct-20 7:48
Member 1497781428-Oct-20 7:48 
GeneralMy vote of 5 Pin
@theRouxster2-Jul-20 2:17
@theRouxster2-Jul-20 2:17 
QuestionThis only works in forms Pin
Member 1482626118-May-20 13:32
Member 1482626118-May-20 13:32 
QuestionProblem with server opening a file upon receiving a client message to do so Pin
Michael B Pliam23-May-19 16:01
Michael B Pliam23-May-19 16:01 
AnswerRe: Problem with server opening a file upon receiving a client message to do so Pin
Michael B Pliam24-May-19 11:09
Michael B Pliam24-May-19 11:09 
AnswerHappy to modify it for mono under Linux Pin
Mei You Qian18-Jul-17 23:36
professionalMei You Qian18-Jul-17 23:36 
SuggestionAccess denied Or Server unable to get message(s) more than one Pin
Sri Nitish3-Mar-16 21:52
Sri Nitish3-Mar-16 21:52 
QuestionBuffer size Pin
Raphael Carubbi29-Feb-16 15:03
Raphael Carubbi29-Feb-16 15:03 
QuestionWhy is It working for the first-time only? Pin
Sri Nitish24-Feb-16 23:43
Sri Nitish24-Feb-16 23:43 
GeneralMy vote of 2 Pin
zipwax26-Dec-15 12:55
zipwax26-Dec-15 12:55 
GeneralRe: My vote of 2 Pin
Nemo196616-Mar-16 6:20
Nemo196616-Mar-16 6:20 
GeneralWell written Pin
echosteg.com2-Oct-15 20:47
echosteg.com2-Oct-15 20:47 
GeneralMy vote of 5 Pin
kezitor16-Sep-15 16:14
kezitor16-Sep-15 16:14 
GeneralMy vote of 5 Pin
Member 1079835230-Jul-14 23:39
Member 1079835230-Jul-14 23:39 
QuestionSupport for multiple servers Pin
AnalogKid1726-Jun-14 11:48
AnalogKid1726-Jun-14 11:48 
AnswerRe: Support for multiple servers Pin
Mick Conroy25-Mar-15 23:15
Mick Conroy25-Mar-15 23:15 
QuestionMake it two ways communication Pin
rezer12-Mar-14 18:42
rezer12-Mar-14 18:42 
AnswerRe: Make it two ways communication Pin
Roger C Moore28-Apr-14 9:51
Roger C Moore28-Apr-14 9:51 
SuggestionMaking a background worker for a web application that uses named pipe to communicate. Pin
3rdey330-Nov-13 21:27
3rdey330-Nov-13 21:27 
GeneralMy vote of 5 Pin
ChrDressler28-Sep-13 6:01
ChrDressler28-Sep-13 6:01 
GeneralMy vote of 5 Pin
Thierry Bachmann2-Sep-13 5:18
Thierry Bachmann2-Sep-13 5:18 
GeneralThanks Pin
Jaime Stuardo - Chile24-Aug-13 16:52
Jaime Stuardo - Chile24-Aug-13 16:52 
GeneralMy vote of 5 Pin
Joezer BH22-Jun-13 23:01
professionalJoezer BH22-Jun-13 23:01 
GeneralMy vote of 5 Pin
JerryShiu28-Apr-13 23:12
JerryShiu28-Apr-13 23:12 

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

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