简体   繁体   English

在IIS 7中使用net.tcp绑定托管WCF服务(无法从外部访问)

[英]Hosting a WCF service using net.tcp binding in IIS 7 (unreachable from outside)

I have developped a WCF duplex service and a Windows Winforms client communicating togeteher via a net.tcp duplex binding. 我已经开发了一个WCF双工服务和一个Windows Winforms客户端,通过net.tcp双工绑定与togeteher进行通信。

Both do communicate and work fine on my LAN, the WCF service beeing hosted on a IIS 7 on a Windows 8 workstation. 两者都在我的LAN上进行通信和正常工作,WCF服务在Windows 8工作站上的IIS 7上托管。

I then tried to host the WCF service on the web on a rent dedicated server running Windows server 2008 R2 with a fixed IP adress: (94.23.220.199) with IIS 7 running and .Net 4.5.2. 然后,我试图在运行Windows Server 2008 R2的租用专用服务器上使用固定IP地址托管WCF服务:(94.23.220.199),运行IIS 7和.Net 4.5.2。

The WCF service has been installed on the /ScgBroadcastorService virtual path and the net.tcp protocol has been activated. WCF服务已安装在/ ScgBroadcastorService虚拟路径上,并且已激活net.tcp协议。 (Actually, all the IIS configuration stuff has been made exactly like on my personnal IIS on my LAN). (实际上,所有的IIS配置都与我局域网上的个人IIS完全一样)。 So the service should be reachable from the outside on the following URL: " http://94.23.220.199/ScgBroadcastorService/Service.svc ". 因此,应该可以从以下URL上的外部访问该服务:“ http://94.23.220.199/ScgBroadcastorService/Service.svc ”。

If you access this link from your browser, you'll get a correct "ScgBroadcastorService Service" page with the two wsdl links. 如果您从浏览器访问此链接,您将获得一个带有两个wsdl链接的正确“ScgBroadcastorService服务”页面。 (These links correctly refer to the "94.23.220.199" IP address. (这些链接正确地引用“94.23.220.199”IP地址。

If one clicks on this links one correctly gets the wsdl xml document. 如果单击此链接,则可以正确获取wsdl xml文档。

So since the wsdl document may be accessed from the outside, I expect the client to be able to communicate with the WCF service.. 因此,由于可以从外部访问wsdl文档,我希望客户端能够与WCF服务进行通信。

But if I launch the client, I get the following exception: (Sorry my home computer is localized in french... The root exception is "The server rejected the client credentials.") 但是,如果我启动客户端,我会得到以下异常:(抱歉,我的家用计算机已本地化为法语...根例外是“服务器拒绝了客户端凭据。”)

Here is the full trace: 这是完整的痕迹:

System.ServiceModel.Security.SecurityNegotiationException: Le serveur a rejeté les informations d'identification du client. ---> System.Security.Authentication.InvalidCredentialException: Le serveur a rejeté les informations d'identification du client. ---> System.ComponentModel.Win32Exception: La tentative d’ouverture de session a échoué
   --- Fin de la trace de la pile d'exception interne ---
   à System.Net.Security.NegoState.ProcessReceivedBlob(Byte[] message, LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.StartReceiveBlob(LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.StartSendBlob(Byte[] message, LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.CheckCompletionBeforeNextSend(Byte[] message, LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.ProcessReceivedBlob(Byte[] message, LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.StartReceiveBlob(LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.StartSendBlob(Byte[] message, LazyAsyncResult lazyResult)
   à System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)
   à System.Net.Security.NegotiateStream.AuthenticateAsClient(NetworkCredential credential, ChannelBinding binding, String targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
   à System.Net.Security.NegotiateStream.AuthenticateAsClient(NetworkCredential credential, String targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
   à System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
   --- Fin de la trace de la pile d'exception interne ---

Server stack trace: 
   à System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
   à System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorBase.InitiateUpgrade(Stream stream)
   à System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, IConnection& connection, ClientFramingDecoder decoder, IDefaultCommunicationTimeouts defaultTimeouts, TimeoutHelper& timeoutHelper)
   à System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(IConnection connection, ArraySegment`1 preamble, TimeoutHelper& timeoutHelper)
   à System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(IConnection connection, TimeoutHelper& timeoutHelper)
   à System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
   à System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
   à System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   à System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   à System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   à System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
   à System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
   à System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   à System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   à System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Note that if I launch the client directly from the host computer, with the same client config file, the client connects and communicates perfectly ! 请注意,如果我直接从主机启动客户端,使用相同的客户端配置文件,客户端将完美地连接和通信!

Here is the web.config file currently installed on the server hosting the service: 以下是当前安装在托管服务的服务器上的web.config文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="ScgServiceLibrary.ScgBroadcastorService">
        <endpoint binding="netTcpBinding" contract="ScgServiceLibrary.IScgBroadcastorService">
          <identity>
            <servicePrincipalName value="host/94.23.220.199" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
   <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

And here is the client config file I've been using from the outside and from the host: 这是我从外部和主机使用的客户端配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
              <binding name="NetTcpBinding_IScgBroadcastorService">
                <security mode="None"></security>
              </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://94.23.220.199/ScgBroadcastorService/Service.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IScgBroadcastorService"
                contract="ScgServiceLibrary.IScgBroadcastorService" name="NetTcpBinding_IScgBroadcastorService">
                <identity>
                    <servicePrincipalName value="host/94.23.220.199" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Note that I have added the 请注意,我添加了

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

line at the end of the web.config file on the server to get a service page with the IP address on the two wsdl links. 在服务器上的web.config文件末尾添加一行,以获取两个wsdl链接上具有IP地址的服务页面。 Without this line the two links include the computername "ns304385" instead of the IP address and of course the wsdl cannot be got from the outside. 没有这一行,这两个链接包括计算机名“ns304385”而不是IP地址,当然wsdl不能从外部获取。

Thank you to help mo to solve the remaining deployment issues.. I'm now stucked and don't know what to do to allow my client to reach my WCF service hosted on the net... 感谢您帮助我解决剩余的部署问题。我现在陷入困境,不知道如何让我的客户端到达我在网络上托管的WCF服务...

Ok I finally solved the issue in the middle of the night... 好的,我终于在半夜解决了这个问题......

I had to turn off the security of the netTcpBinding on both side. 我不得不关闭netTcpBinding的安全性。

But it was not so simple to find out how to turn it off on the server side for a contract requiring duplex communication. 但要找到如何在服务器端为需要双工通信的合同关闭它并不是那么简单。

Here is the web.config file: 这是web.config文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="customTcpBinding" maxReceivedMessageSize="5242880" maxConnections="10">
          <readerQuotas maxDepth="64" maxStringContentLength="5242880" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
          <security mode="None"></security>
        </binding>
      </netTcpBinding>
    </bindings>
    <services>
      <service name="ScgServiceLibrary.ScgBroadcastorService">
        <endpoint binding="netTcpBinding" bindingConfiguration="customTcpBinding" contract="ScgServiceLibrary.IScgBroadcastorService">
          <identity>
            <servicePrincipalName value="host/94.23.220.199" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
   <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

The trick was to add a customTcpBinding with the security mode set to "None" and to refer this new binding in the endpoint with the bindingConfiguration attribute. 诀窍是添加一个设置为“None”的安全模式的customTcpBinding,并使用bindingConfiguration属性在端点中引用这个新绑定。

Not sure wether all the parameters of the customTcpBinding are optimal, but they are ok for a duplex contract. 不确定customTcpBinding的所有参数是否最佳,但它们适用于双工合同。 (My first attempt was refused for a duplex contract) (我的第一次尝试因双工合同而被拒绝)

On the client side I also had to set the security mode of the binding to "None". 在客户端,我还必须将绑定的安全模式设置为“None”。 Here is my new config file on the client side: 这是我在客户端的新配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_IScgBroadcastorService">
                    <security mode="None"></security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://94.23.220.199/ScgBroadcastorService/Service.svc"
                binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IScgBroadcastorService"
                contract="ScgServiceLibrary.IScgBroadcastorService" name="NetTcpBinding_IScgBroadcastorService">
                <identity>
                    <servicePrincipalName value="host/94.23.220.199" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

And finally my WCF Windows WinForm client works fine and communicate with my Duplex WCF Service !!! 最后我的WCF Windows WinForm客户端工作正常,并与我的Duplex WCF服务进行通信!

I have to say that solving this issue was a real nightmare... 我不得不说解决这个问题是一场真正的噩梦......

Hope this complete description of my issue will help other developpers trying to host their duplex net.Tcp binded WCF service on the net and bored with all the unexpected exception before finding the right way to modify their config files. 希望这个问题的完整描述将有助于其他开发人员尝试托管他们的双工网络.Tcp绑定网络上的WCF服务,并在找到正确的修改配置文件的方法之前厌倦了所有意外的异常。

Good night folks ! 晚安伙计们! Only three hours to sleep before returning to job... :-( 回到工作岗位前只睡了三个小时... :-(

setting security mode to "none" is not the way you should solve this because setting security to None will also remove confidentiality (encryption) and integrity (signing) of your messages. 将安全模式设置为“none”不是解决此问题的方法,因为将安全性设置为None也会删除邮件的机密性(加密)和完整性(签名)。

<security mode="None"></security>

To remove authentication you can either: 1) set the security mode to message 要删除身份验证,您可以:1)将安全模式设置为message

<bindings>
    <netTcpBinding>
        <binding name="NetTcpBinding_IScgBroadcastorService">
            <security mode="Message">
                <message clientCredentialType="None" />
            </security>
        </binding>
    </netTcpBinding>
</bindings>

2) or set the security mode to Transport 2)或将安全模式设置为Transport

<bindings>
    <netTcpBinding>
        <binding name="NetTcpBinding_IScgBroadcastorService">
            <security mode="Transport">
                <transport clientCredentialType="None" />
            </security>
        </binding>
    </netTcpBinding>
</bindings>

I have to admit that I didn't try this with your code. 我必须承认,我没有尝试使用您的代码。 Just want to make sure people reading this do know that authentication and message security are not the same thing and point them in the right direction. 只是想确保阅读本文的人确实知道身份验证和消息安全性并不是一回事,并指出它们是正确的方向。

I had the same error for a duplex wcf service, hosted in a windows service on a remote machine. 我在远程计算机上的Windows服务中托管的双工wcf服务有同样的错误。 To make it work I had to create Inbound and Outbound rules to open the service port in Windows Firewall advanced settings. 为了使其工作,我必须创建入站和出站规则以在Windows防火墙高级设置中打开服务端口。 Also I've created a server user and included this code in my client: 我还创建了一个服务器用户,并将此代码包含在我的客户端中:

this._client = new WcfService.WcfServiceClient(context);
_client.ClientCredentials.Windows.ClientCredential.UserName = user;
_client.ClientCredentials.Windows.ClientCredential.Password = password;

WOW ! 哇 ! I just found a partial solution ! 我刚刚找到了部分解决方案!

At least an explanation of why I cannot reach the WCF service from the outside ! 至少解释为什么我无法从外面到达WCF服务!

Reading this: https://social.msdn.microsoft.com/Forums/vstudio/en-US/1551b4e1-8e15-4da2-b155-d398379809b3/the-server-has-rejected-the-client-credentials-in-wcf?forum=wcf 阅读本文: https//social.msdn.microsoft.com/Forums/vstudio/en-US/1551b4e1-8e15-4da2-b155-d398379809b3/the-server-has-rejected-the-client-credentials-in-wcf ?论坛= WCF

So I tried to create an account on the server with the same name and password than the account I'm using on my home computer when I try to launch the client... and... It works !!! 因此,当我尝试启动客户端时,我尝试使用与我在家用计算机上使用的帐户相同的名称和密码在服务器上创建一个帐户......并且...它有效!

Of course you'll agree with me this is not acceptable... 当然你会同意我这是不可接受的......

I want my WCF service beeing able to accept clients connexions from user logged from anywhere on the net and using their own username/password I do not want to care ! 我希望我的WCF服务能够接受来自网上任何地方的用户的客户连接,并使用他们自己的用户名/密码,我不想在意!

So, the search continues on another way... How to allow WCF client connections without having to create accounts on my server ? 所以,搜索继续以另一种方式...如何允许WCF客户端连接而不必在我的服务器上创建帐户?

Stay tuned... hope will soon find the answer... 请继续关注...希望很快能找到答案......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM