Click here to Skip to main content
15,867,594 members
Articles / Programming Languages / C#

Many to One Local IPC using WCF and NetNamedPipeBinding

Rate me:
Please Sign up or sign in to vote.
5.00/5 (13 votes)
8 Mar 2010CPOL4 min read 70.6K   2.5K   25   14
I needed to talk between one server process and multiple client processes, this was a simple test harness to prove it

Introduction

I used WCF with the NetNamedPipeBinding for IPC between my server process and multiple clients that would check in and register with the server.

The clients needed to be able to invoke methods on the server - with or without args and return values, and the server needed to be able to invoke methods on the client in similar ways.

In the real world application example, I didn't have a way for the clients to unregister before they exited, so that was done server side.

Here is a picture of it in operation.

Both clients have registered - you can see their GUIDs in the listbox on the server.

One of the clients has sent a message in, the other has sent an anonymous message, then the first client has requested the anonymous message. 

The server has sent a broadcast message to all registered clients.

serverclient.jpg

Background

The WCF stuff seemed very simple to begin with, however I found before long that I had to trawl through a few articles to get things working correctly - and as my first foray into WCF, it may not be completely best practice - I hope I learn from comments if it isn't!

To understand this, you don't need to understand any details about named pipes other than that they are something that runs locally on your computer, and that WCF sits above it and hides all of the details.

The most important thing is to understand that a WCF communication consists of two endpoints (a client and a server), and a channel that allows communication to flow in between them. In this case, the channel is using a named pipe to facilitate transport of messages.

Additionally none of this WCF is like COM - it doesn't require marshalling or serialization of message objects, and if you invoke an object method on the server, it is running it inside the process that is hosting the server and returning the result - you don't really need to worry about how it is happening - well, I didn't anyway.

Using the Code

The code is really not useful other than to see as a working example, it isn't something you would link into an existing application, and probably isn't even a very good starting point for a new application, however it is what it is and allows you to watch what is going on under several different messaging scenarios.

The first part is messages that start on the client - usually the first message would be a "Register" message that gives a Guid to identify the client to the server. I mark this as OneWay so that WCF knows not to wait for a reply.

C#
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
    [OperationContract(IsOneWay = true)]
    void Register(Guid clientID);

    [OperationContract(IsOneWay = true)]
    void DisplayTextOnServer(string text);

    [OperationContract(IsOneWay = true)]
    void DisplayTextOnServerAsFromThisClient(Guid clientID, string text);

    [OperationContract]
    string GetLastAnonMessage();
}

Next is messages that start on the server and go out to the clients - in this case, there is only one - a simple instruction to display some text.

C#
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromServerToClientMessages
{
    [OperationContract(IsOneWay = true)]
    void DisplayTextInClient(string text);
}

Clients are started with the GUID as part of their URI. The server is much the same but just called "Server" (i.e. no GUID). Note that all I need to do is implement the interface and start a service host for the object, add a service endpoint, and open the connection. It is then able to receive channel connections. At exit, there really should be a corresponding Dispose that closes it down too, but I've left that out of the example.

C#
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, 
	InstanceContextMode = InstanceContextMode.Single)]
public partial class ClientForm : Form, IFromServerToClientMessages
{
    Guid _clientID;
    ServiceHost _clientHost;

    public ClientForm()
    {
        InitializeComponent();

        _clientID = Guid.NewGuid();
        _clientHost = new ServiceHost(this);
        _clientHost.AddServiceEndpoint((typeof(IFromServerToClientMessages)), 
		new NetNamedPipeBinding(), "net.pipe://localhost/Client_" + 
		_clientID.ToString());
        _clientHost.Open();
    }

A basic message from client to server has the following code - articles I read by others in other forums implied that closing the channel between messages was the way to go.

C#
public void Register(Guid clientID)
{
    using (ChannelFactory<IFromClientToServerMessages> factory = 
	new ChannelFactory<IFromClientToServerMessages>(new NetNamedPipeBinding(), 
	new EndpointAddress("net.pipe://localhost/Server")))
    {
        IFromClientToServerMessages clientToServerChannel = factory.CreateChannel();

        try
        {
            clientToServerChannel.Register(clientID);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
        finally
        {
            CloseChannel((ICommunicationObject)clientToServerChannel);
        }
    }
}

private void CloseChannel(ICommunicationObject channel)
{
    try
    {
        channel.Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        channel.Abort();
    }
}

Points of Interest

Closing the channel is important - you can easily get a sample working, but before long you find that after 10 minutes, you start hitting timeouts. Then you go down the path of increasing the timeout or trying to use reliable session, but NetNamedPipeBinding is a bit different. Next you try keepalives, but you find that if you step through it with the debugger you hit the timeouts quite easily. Then you read more forums and come to the conclusion that closing it each time isn't too much of an overhead - sure a lot of copy pasted code, but it works. Well that's how it was for me anyhow, your mileage may vary!

Also, if you get a timeout exception, it means that the other end of the channel did not respond within a minute - this usually means that you've got something trying to talk back the other way at the same time that is blocking. The exceptions look like this:

 System.TimeoutException: The read from the pipe did not complete 
	within the allotted timeout of 00:01:00. The time allotted to this 
	operation may have been a portion of a longer timeout. ---> 
	System.IO.IOException: The read operation failed, see inner exception. ---> 
	System.TimeoutException: The read from the pipe did not complete within the 
	allotted timeout of 00:01:00. The time allotted to this operation may have 
	been a portion of a longer timeout.
   at System.ServiceModel.Channels.PipeConnection.WaitForSyncRead
	(TimeSpan timeout, Boolean traceExceptionsAsErrors)
   at System.ServiceModel.Channels.PipeConnection.Read(Byte[] buffer, 
	Int32 offset, Int32 size, TimeSpan timeout)
   at System.ServiceModel.Channels.DelegatingConnection.Read(Byte[] buffer, 
	Int32 offset, Int32 size, TimeSpan timeout)
   at System.ServiceModel.Channels.ConnectionStream.Read(Byte[] buffer, 
	Int32 offset, Int32 count, TimeSpan timeout)
   at System.ServiceModel.Channels.ConnectionStream.Read(Byte[] buffer, 
	Int32 offset, Int32 count)
   at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Security.NegotiateStream.StartFrameHeader(Byte[] buffer, 
	Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.NegotiateStream.StartReading(Byte[] buffer, 
	Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.NegotiateStream.ProcessRead(Byte[] buffer, 
	Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
   --- End of inner exception stack trace ---
   at System.Net.Security.NegotiateStream.ProcessRead(Byte[] buffer, 
	Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.NegotiateStream.Read(Byte[] buffer, 
	Int32 offset, Int32 count)
   at System.ServiceModel.Channels.StreamConnection.Read(Byte[] buffer, 
	Int32 offset, Int32 size, TimeSpan timeout)
   --- End of inner exception stack trace ---
Server stack trace: 
   at System.ServiceModel.Channels.StreamConnection.Read(Byte[] buffer, 
	Int32 offset, Int32 size, TimeSpan timeout)
   at System.ServiceModel.Channels.SessionConnectionReader.Receive(TimeSpan timeout)
   at System.ServiceModel.Channels.SynchronizedMessageSource.Receive(TimeSpan timeout)
   at System.ServiceModel.Channels.FramingDuplexSessionChannel.OnClose
	(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnClose(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Close()
Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage
	(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke
	(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Close()

History

  • 8th March, 2010: Article first cut written

License

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


Written By
New Zealand New Zealand
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
1-Oct-20 11:20
Member 149536741-Oct-20 11:20 
Question10 Year Later and Still a Big Thank You Pin
darbid7-Mar-20 8:32
darbid7-Mar-20 8:32 
QuestionCan't Connect in Local Network Endpoint not found exception Pin
pranav_patel17-Feb-14 20:37
pranav_patel17-Feb-14 20:37 
AnswerRe: Can't Connect in Local Network Endpoint not found exception Pin
Lance Roberts18-Feb-14 8:17
Lance Roberts18-Feb-14 8:17 
GeneralRe: Can't Connect in Local Network Endpoint not found exception Pin
pranav_patel19-Feb-14 18:28
pranav_patel19-Feb-14 18:28 
GeneralRe: Can't Connect in Local Network Endpoint not found exception Pin
miguel sadf29-Sep-15 0:21
miguel sadf29-Sep-15 0:21 
QuestionGreat Solution - Thanks for posting! Pin
Ken74715-Aug-12 8:16
Ken74715-Aug-12 8:16 
QuestionMy vote of 5 Pin
ezgar14-Aug-12 17:34
ezgar14-Aug-12 17:34 
GeneralJust what I Need Pin
Rolando CC3-Aug-12 15:40
professionalRolando CC3-Aug-12 15:40 
QuestionA doubt Pin
theCPkid20-Jun-12 3:57
theCPkid20-Jun-12 3:57 
AnswerRe: A doubt Pin
Lance Roberts24-Jul-12 16:04
Lance Roberts24-Jul-12 16:04 
this example isn't full duplex WCF like you are describing. The clients are also servers - they give the central server their address, so when the central server wants to get back in contact it acts as a client to them. There are no connections held open, and no timeout issues etc, which was the problem I needed to solve at the time.
GeneralDuplex Pin
Chris Di11-Nov-10 1:09
Chris Di11-Nov-10 1:09 
GeneralThanks man - nice work. Pin
JimShat4-Nov-10 9:38
JimShat4-Nov-10 9:38 
GeneralThanks Pin
vspin5-Apr-10 18:49
vspin5-Apr-10 18:49 
GeneralGreat work!!! Pin
RAND 4558668-Mar-10 6:08
RAND 4558668-Mar-10 6:08 

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.