An Unanticipated Addendum for Certain MEX Scenarios

I wrote the article on MEX endpoints about a month ago after someone asked the question on one of our internal mailing lists. I wrote the NetTcp Port Sharing sample about a month ago as well. Late at night I needed to test that the MEX endpoint code compiled before I posted the article so I just dropped it into the port sharing sample and tried it. Funny things can happen when you use two obscure features together.

Of course, I picked the MEX address to use the same port as the regular client contract in the sample.

 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(behavior);
Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
string mexAddress = String.Format("net.tcp://localhost:5555/mex/{0}", salt);
host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexAddress);

This works just fine when port sharing is turned off. Two endpoints in the same service host can share the same port without conflict. The port sharing feature is only needed to coordinate the sharing of ports across process boundaries. However, when you turn port sharing on, this breaks with an AddressAlreadyInUseException saying that there may already be someone listening on that IP address. An unlikely assertion! It turns out in this case that adding global port sharing actually prevents you from performing local port sharing.

The reason for this is a bit non-obvious. Port sharing allows multiple processes to share the same port (obvious). However, every listener on the port must buy into the sharing arrangement. It wouldn't work for three programs to share a port but a fourth to come in and demand exclusive access (obvious). What's going wrong is that the MexTcpBinding has its own TCP transport with its own port sharing setting that is still set to the default off position (not obvious). One of the endpoints is trying to share the port with other processes but the other is demanding exclusive access for this process, causing the exception message that you see (painful). The workaround for this is to enable port sharing on the MEX binding as well (ugly).

 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
host.Description.Behaviors.Add(behavior);
Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
CustomBinding mexBinding2 = new CustomBinding(mexBinding);
mexBinding2.Elements.Find<TcpTransportBindingElement>().PortSharingEnabled = true;
string mexAddress = String.Format("net.tcp://localhost:5555/mex/{0}", salt);
host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding2, mexAddress);

The equivalent in configuration follows the same principle by crafting a custom binding for MEX with the port sharing setting enabled.