Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
261 views
in Technique[技术] by (71.8m points)

c# - Setting up WCF TCP service in a web application

I've been battling with this for days, literally going through a hundred articles giving partial guidelines on how to set up a WCF TCP based service in a web application. If someone can help me, I will make this question into a complete guideline.

Current status

The net.tcp connection works on my development machine. It also works locally after being deployed to a Windows Server 2008 R2. However, it doesn't work remotely, even though it's possible to telnet to port 808 on the server remotely. Scroll to the bottom of the question for details. Please help if you can.

I will create a new question for this detail and update this question with the answer if I get a result out of it.

Code

I created ServerHubService.svc with the following content:

namespace Manage.SignalR
{
    [ServiceContract]
    public class ServerHubService
    {
        [OperationContract]
        public void UpdateServerStatus(string serverStatus)
        {
            // Do something
        }
    }
}

Configuration of the application hosting the service

Following an online tutorial, I added the following to my Web.config (I tried MANY different variations). This is the Web.config of the web application hosting the service that I later want to connect to with TCP.

<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="ServerHubBehavior"
      name="Manage.SignalR.ServerHubService">
        <endpoint address=""
              binding="netTcpBinding"
              bindingConfiguration="portSharingBinding"
              name="MyServiceEndpoint"
              contract="Manage.SignalR.ServerHubService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>

        <endpoint address="mex"
              binding="mexTcpBinding"
              bindingConfiguration=""
              name="MyServiceMexTcpBidingEndpoint"
              contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://test.mydomain.com:808/SignalR/ServerHubService.svc" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServerHubBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="portSharingBinding" portSharingEnabled="true"/>
      </netTcpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
      minFreeMemoryPercentageToActivateService="0" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

httpGetEnabled="true" is important because otherwise you cannot create a service reference to the service.

Configuration of the server where the web application is running

I am setting this up on my development machine which has IIS 7.5

I read that IIS Express (built into Visual Studio) doesn't support net.tcp, so I set up a new website in IIS 7.5 using .NET 4.5 and that has a net.tcp binding

bindings in IIS

I also went to Advanced Settings for the website and set Enabled Protocols to http,net.tcp

I ensured that the Windows Feature Non-HTTP Activation is enabled (and restarted). It's a Windows Feature so look for "Turn Windows features on or off" to find this.

enter image description here

Confirming the web application is running

The website works fine for the rest of the web application. I set up test.mydomain.com to point to 127.0.0.1 in my hosts file. I can even visit http://test.mydomain.com/SignalR/ServerHubService.svc and it will show me a nice auto generated page from .NET, explaining how to use this service.

So far so good.

The page generated by .NET tells me to use this address to generate a connection to my service:

net.tcp://computername/SignalR/ServerHubService.svc/mex

Trying to connect to the service as a client

If you do not remember to set httpGetEnabled="true" you will get an error when trying to create a service reference to it. If you use the WCF Test Client (also a tool included in Visual Studio) and didn't set httpGetEnabled, you will get an error like the following:

Error: Cannot obtain Metadata from net.tcp://computername/SignalR/ServerHubService.svc/mex

If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation at http://go.microsoft.com/fwlink/?LinkId=65455.WS-Metadata

Exchange Error URI: net.tcp://computername/SignalR/ServerHubService.svc/mex Metadata contains a reference that cannot be resolved: 'net.tcp://computername/SignalR/ServerHubService.svc/mex'. Could not connect to net.tcp://computername/SignalR/ServerHubService.svc/mex. The connection attempt lasted for a time span of 00:00:04.0032289.

TCP error code 10061: No connection could be made because the target machine actively refused it [2001:0:4137:9e76:c81:a4c:a547:b2fd]:808. No connection could be made because the target machine actively refused it [2001:0:4137:9e76:c81:a4c:a547:b2fd]:808

However, if you've done everything as above, you should be able to add a reference to the service.

Calling methods in the service

When trying to call a simple Hello World method in the service from the WCF Test Client, it returns the following error:

Could not connect to net.tcp://computername/SignalR/ServerHubService.svc. The connection attempt lasted for a time span of 00:00:04.0002288. TCP error code 10061: No connection could be made because the target machine actively refused it.

This is inner stacktrace that is included in with the error:

No connection could be made because the target machine actively refused it [2001:0:5ef5:79fb:3884:a:a547:b2fd]:808 at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Connect(EndPoint remoteEP) at System.ServiceModel.Channels.SocketConnectionInitiator.Connect(Uri uri, TimeSpan timeout)

If you use netstat -an |find /i "listening" to see that nothing is listening on port 808, it's probably because the Net.Tcp Listener Adapter service is not running.

Confirmation

The call goes through now, but some confirmation is needed before it can be declared a success. I need to confirm that this is in fact net.tcp call on port 808 and not actually a call on the http endpoint. I am trying to do this with Wireshark, but it doesn't show up, probably because it's a call happening from and to my local machine.

Deployment

The last challenge to conquer would be to deploy this on a web server, ensuring that what works on the development machine, also works on the web server.

It doesn't work after publish to a Windows Server 2008 R2. It gives the common SocketException: An existing connection was forcibly closed by the remote host.

It works well locally on the server, but it doesn't work remotely.

Here is the checklist used to check the server:

  • Is the Net.Tcp Listener Adapter service running? Yes
  • Is the IIS site binding to net.tcp set to 808:*? Yes
  • Is Enabled Protocols under Advanced Settings for the site in IIS set to http,net.tcp? Yes
  • Is the server listening on port 808? Yes, checked with netstat -an |find /i "listening"
  • Is a port 808 open in the firewall? Yes.
    • Firewall is turned off on the server.
    • I can telnet to the server on port 808 from outside with telnet mydomain.com 808
  • In the configuration of the service on the server, the following was confirmed:
    • baseAddress is adjusted to net.tcp://mydomain.com:808/SignalR/ServerHubService.svc
    • This was localhost before, but changed to mydomain.com after it didn't work on the server: <identity><dns value="mydomain.com" /></identity>
  • It has been tested with a client locally on the server and on another server. Both can connect to port 808 with telnet and both give the same error message.

What configuration could be missing on the server? I am so close to the goal. Please assist me in troubleshooting this and complete the question.

Here is the client configuration on the server calling the service:

<system.serviceModel>
  <bindings>
    <netTcpBinding>
      <binding name="MyServiceEndpoint" />
    </netTcpBinding>
  </bindings>
  <client>
    <endpoint address="net.tcp://mydomain.com:808/SignalR/ServerHubService.svc"
      binding="netTcpBinding" bindingConfiguration="MyServiceEndpoint"
      contract="ServerHubService.ServerHubService" name="MyServiceEndpoint">
      <identity>
        <dns value="mydomain.com" />
      </identity>
    </endpoint>
  </client>
</system.serviceModel>

I also tried without 808 configured in the endpoint, as this is rumored to be the default port for net.tcp connections. However, it still gives the same result.

Instead of trying lots of things randomly, the good question is perhaps: How does one debug something like this? Can I install a program on either server that will tell me exactly WHY this call is being blocked?

With Wireshark configured like this, it's possible to look at the call coming to the server on port 808 and possibly determine why the call doesn't work. However, I have no idea how to analyze this at present time.

wireshark

Good luck

I gave up and implemented this in the socket layer instead. It worked immediately. I hope this can help someone else, though. I'm setting an answer because many of the issues were solved and it did work locally eventually, so any problem remaining is probably related to me specific environment.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have to generate the Proxy based on the metadata exposed over HTTP for tcp.(make sure httpGetEnabled is set to true). The address you use at the client should be the hosted address. Please refer the below post.

http://blogs.msdn.com/b/swiss_dpe_team/archive/2008/02/08/iis-7-support-for-non-http-protocols.aspx


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...