Search code examples
c#wcfremotingnamed-pipes

Two unique named pipes conflicting and InvalidCredentialException


There are two problems I ran into last night which I have resolved now, but I am not 100% sure as to why what I have done has resolved them and was hoping maybe someone could offer some insight as I've been turning over a lot of rocks and have had no luck!

First Problem

The first issue is that I had two uniquely named pipes that were in two separate programs:

  • net.pipe://localhost/superuniquepipe1
  • net.pipe://localhost/superuniquepipe2

However, the second program to launch would throw an exception (I believe it was AddressAlreadyInUseException) when opening the ServiceHost due to the address already being in use.

The way I was instantiating these ServiceHosts was as follows:

Uri[] baseAddresses = new Uri[] { new Uri("net.pipe://localhost") };
this.host = new ServiceHost(this, baseAddresses);
this.host.AddServiceEndpoint(typeof(IHostType), new NetNamedPipeBinding(), "superuniquepipe1");
this.host.Open();

So I'd specify the base address of localhost first, and then specify the rest of it when adding the endpoint, the way I resolved this was to change the code as follows:

this.host = new ServiceHost(this);
this.host.AddServiceEndpoint(typeof(IHostType), new NetNamedPipeBinding(), "net.pipe://localhost/superuniquepipe2");
this.host.Open();

Am I correct in saying the reason this worked is because it was checking only the base addresses and not the endpoint I was trying to add? And is using the second code sample a valid / safe way to have multiple programs listening on "localhost"?

Second Problem:

In an attempt to fix the above, I had changed the base address from localhost to a number of different unique strings e.g. "net.pipe://rawrwhyisntthisworkingsadface", but when doing this I'd be presented with an InvalidCredentialException from the client trying to establish a connection (see below code)

I was under the impression a named pipe can literally be named anything, can anyone shed some light on this one?

ChannelFactory<IHostType> factory = new ChannelFactory<IHostType>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://rawrwhyisntthisworkingsadface/superuniquepipe2"));
IHostType proxy = factory.CreateChannel();
proxy.CallSomeMethodAndGetAnException();

Any input would be greatly appreciated, as I said I have resolved the issue and just want to know why my solution worked, but if you see a flaw in how I've resolved it and can suggest a better way of doing it please do so :)


Solution

  • Re problem 1:

    The WCF NetNamedPipeBinding uses a named shared memory section to publish to its clients the actual name of the pipe over which the service can be called. The pipe name itself is a GUID, generated afresh each time the service host is opened. It is the name of the shared memory section used to publish the service which is dependant on the service URL. If a base address is defined, the base address is used to derive this name.

    This means you can only ever have one WCF service application at a time running which uses a particular base address for its NetNamedPipe endpoints. If you try to start a second one, it fails with AddressAlreadyInUseException because it finds that the name WCF wants to use for the publishing location (derived from the base address) has already been taken by another application.

    If you specify no base address, and give each service an absolute, unique service URL, then the name of the publishing location is now derived from the full absolute URL, and there is no name clash between the applications. This is an entirely valid and safe way to have multiple WCF named pipe services listening.

    Re problem 2:

    On the service side you can use anything for the host name part of the service URL. This is due to the HostNameComparisonMode setting applied by default in the NetNamePipeBinding, since the algorithm in WCF which derives the name for the shared memory publishing location substitutes a wildcard character for the host name see here to enable the configured host name comparison mode to be implemented.

    On the client side, however, the service URL is constrained: the host part must genuinely resolve to localhost (i.e. it is localhost, the correct IP address, or the correct machine name).