Partager via


.Net TCP/IP Port Sharing

As stated in the .Net TCP Port Sharing MSDN article, “Windows Communication Foundation (WCF) provides a new TCP-based network protocol (net.tcp://) for high-performance communication. WCF also introduces a new system component, the Net.TCP Port Sharing Service that enables Net.TCP ports to be shared across multiple user processes.”

I recommend to have a look at the whole article, as it’s quite interesting; however what basically comes out is that we have a Windows service (Net TCP Port Sharing Service) implemented in SmsVcHost.exe which behaves more or less as http.sys in terms of connections and port sharing, but instead of taking care of HTTP connections, SmsVcHost is interested in .Net Tcp ones.

The fundamental result is that you can have several WCF services adopting the .Net TCP binding which share the same port, no matter where those services are hosted in (IIS, self- hosted, etc.) since the SmsVcHost receives the incoming connections and dispatch the messages to the right service (of course the services have to register themselves through the SmsVcHost at the beginning).

Just be aware this is not working at that way by default: you first have to enable and configure the .Net TCP Port Sharing Service, then you have to properly configure your WCF service using a NetTcpBinding through the PortSharingEnabled configuration property.
Here are the steps documented in MSDN:

I’d like to draw your attention on the Net. TCP Port Sharing configuration, especially this sentence in MSDN:

In general, care should be taken when modifying the contents of the SMSvcHost.exe.config file, because any configuration settings specified in this file affect all of the services on a computer that uses the Net.TCP Port Sharing Service. This includes applications on Windows Vista that use the TCP Activation features of the Windows Process Activation Service (WAS).

However, sometimes you may need to change the default configuration for the Net.TCP Port Sharing Service. For example, the default value for maxPendingAccepts is 2, which is a conservative value. Computers that host a large number of services that use port sharing should increase this value to achieve maximum throughput.

Based on the system workload, you could face a strange error like this:

"An error occurred while dispatching a duplicated socket: this handle is now leaked in the process.
ID: 4972
Source: System.ServiceModel.Activation.TcpWorkerProcess/29805701
Exception: System.TimeoutException: This request operation sent to <https://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous> did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.
Server stack trace:
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeEndService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Activation.WorkerProcess.EndDispatchSession(IAsyncResult result)
Process Name: SMSvcHost
Process ID: 1728"

That basically means the SmsVcHost service is not able to handle with all the required .Net TCP connections, so you should properly tune your SmsVcHost.exe.config file settings:

 <configuration>
   <system.serviceModel.activation>
       <net.tcp listenBacklog="10"
          maxPendingAccepts="2"
          maxPendingConnections="10"
          receiveTimeout="00:00:10"
          teredoEnabled="false">
          <allowAccounts>
             // LocalSystem account
             <add securityIdentifier="S-1-5-18"/>
             // LocalService account
             <add securityIdentifier="S-1-5-19"/>
             // Administrators account
             <add securityIdentifier="S-1-5-20"/>
             // Network Service account
             <add securityIdentifier="S-1-5-32-544" />
             // IIS_IUSRS account (Vista only)
             <add securityIdentifier="S-1-5-32-568"/>
           </allowAccounts>
       </net.tcp>
</configuration>

You must be aware of how many WCF services have registered themselves against the .NET Tcp Port Sharing Services and how many connections could be established by each one. For instance a setting like this could solve the problem and make the aforementioned exception disappear:

 <net.tcp listenBacklog="100" maxPendingConnections="1000" maxPendingAccepts="10" receiveTimeout="00:00:10" teredoEnabled="false">

Maybe the maxPendingConnection could have been set to a lower value (2-300), but changing maxPendingAccepts from 2 to 10 has probably been the solution. Only after testing we could say the last word on the best tuning.

/----------------------/

And now I’d like to share a short Q/A list I wrote for a customer.

Q1: There is no api to configure the tcp-port-sharing service ("App.config File"). Different applications can set the parameters in this file differently.

        (for example

            listenBacklog="1"

            maxPendingConnections="1"

            maxPendingAccepts="1"

            receiveTimeout="00:00:10"

            teredoEnabled="false")

A1: Yes that's correct. This is due to the .Net TCP Port Sharing architecture, which uses the SMSvcHost.exe service (https://msdn.microsoft.com/en-us/library/aa702669.aspx).

Please be aware when using Http Port Sharing, WCF employs http.sys exactly as IIS 6.0 does, even though it's a self hosted web service. From: https://msdn.microsoft.com/en-us/library/ms730158.aspx:

"But because WCF and IIS 6.0 both use the kernel-mode HTTP stack (HTTP.sys), IIS 6.0 can share port 80 with other self-hosted WCF services running on the same machine".

If you change the http.sys configuration (through httpcfg/netsh or the registry: https://support.microsoft.com/Default.aspx?id=820129) you'll affect every application placed on top of it. By the way I understand your concern: editing a file is generally easier.

Q2: Tcp-Port Sharing is not activated by default.

A2: Yes that's correct. You will need to manually enable the service. Refer to https://msdn.microsoft.com/en-us/library/ms733925.aspx for further details. If the service is disabled, an exception will be thrown when the server application is started:

"Unhandled Exception: System.ServiceModel.CommunicationException: The TransportManager failed to listen on the supplied URI using the NetTcpPortSharing service: failed to start the service because it is disabled. An administrator can enable it by running 'sc.exe config NetTcpPortSharing start= demand'.. ---> System.InvalidOperationException: Cannot start service NetTcpPortSharing on computer '.'. ---> System.ComponentModel.Win32Exception: The service cannot be started, either because it is disabled or because it has no enabled devices associated with it"

Port sharing is enabled on the server by setting the PortSharingEnabled property of the NetTcpBinding or the TCP transport binding element. The client does not need to know how port sharing has been configured to use it on the server.

Q3: the default settings are not good for our application because the Service is running under the context of a specially created user. Thus this user's SID has to be included in the App.config.

In addition, the settings for maxPendingConnections  and  maxPendingAccepts must be increased.

What are recommended parameters for large applications?

A3: As far as concerns the maxPendingConnections and maxPendingAccepts settings, our defaults are intentionally conservative so that customers must opt-in to allowing large amounts of work into the system.

It's hard to recommend some values, because it depends on the applications running on the system; you have to keep into account the user could have some other services using TCP Activation with WAS, which your application has to share SMSvcHost.exe resources with. According to the Product Team, you shouldn't increase “maxPendingAccepts” too much. 5-10 would be a good number. It means it spawns 5-10 concurrent threads to pull connections.

Feel free to increase the maxPendingConnections value according to your needs (you can also set 1000, even though I'd wonder why if you needed so many connections. 100-200 can be considered a good choice).

Q4: If we activate the WAS (because we use a foreign Application that uses WAS with tcp activation), does the WAS also manipulate the App.config file?

A4: Installing WAS + non-HTTP activation shouldn't impact on the SMSvcHost.exe.config settings; to make sure I've arranged a test on my Windows 2008 server: after non-HTTP activation feature install, the SMSvcHost.exe.config hasn't been changed at all.

Q5: the WCF WAS examples in MSDN don’t mention the SMSvcHost.exe.config. Therefore I think the WAS manipulates the SMSvcHost.exe.config automatically.

Is that correct?

What happens if the tcp-Service runs with application pools under an own account?

Who add's the SID to the SMSvcHost.exe.config?

A5: As you can read at https://msdn.microsoft.com/en-us/library/aa702669.aspx:

"By default, permission to use the port sharing service is granted to system accounts (LocalService, LocalSystem, and NetworkService) as well as members of the Administrators group. Applications that allow a process running as another identity (for example, a user identity) to connect to the port sharing service must explicitly add the appropriate SID to the SMSvcHost.exe.config (these changes are not applied until the SMSvc.exe process is restarted)."

With the sample you've mentioned, there's no need to change SMSvcHost.exe.config, as the service should be running as "network service". WAS, when using non-HTTP activation, is based on the default SMSvcHost.exe.config settings, so there's no change automatically made in some way.

If your service is configured to run under an account not included in the SMSvcHost.exe.config file, it's up to you to add the SID into the config file.

Q6: in MSDN at "https://msdn.microsoft.com/en-us/library/ms733109.aspx" I dont't understand:

"the binding settings have to match on the following seven properties:

ConnectionBufferSize

ChannelInitializationTimeout

MaxPendingConnections

MaxOutputDelay

MaxPendingAccepts

ConnectionPoolSettings.IdleTimeout

ConnectionPoolSettings.MaxOutboundConnectionsPerEndpoint

Otherwise, the endpoint that is initialized first always determines the values of these properties, and endpoints added later throw a ServiceActivationException if they do not match those settings."  

What happens, if two different Applications, thas uses WAS and tcp-Activation, use different binding settings? Application A don’t know the code of Application B.

A6: That means if your service exposes more than one endpoint and they use different binding configurations, you have to make them in order to have the mentioned 7 properties set to the same value, otherwise you incur in the ServiceActivationException. This is because the SMSHost acts like a proxy for the service. If you have different bindings being used by different services, you shouldn't face any trouble.

/----------------------/

I hope I’ve clarified most aspects of .Net TCP Port Sharing feature, but if this wasn’t enough, I’d like to advise the following article: Extend Your WCF Services Beyond HTTP With WAS. I consider that a “must” if you want to start learning what happens behind the curtains.

If I’ve left any aspect uncovered, please don’t hesitate to ask!

Andrea

Comments