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

时间:2015-05-20 09:01:23

标签: c# .net wcf iis tcp

我开发了一个WCF双工服务和一个Windows Winforms客户端,通过net.tcp双工绑定与togeteher进行通信。

两者都可以在我的局域网上进行通信和正常工作,WCF服务在Windows 8工作站上的IIS 7上托管。

然后,我尝试在运行Windows Server 2008 R2的租用专用服务器上使用固定IP地址(94.23.220.199)运行IIS 7,运行IIS 7和.Net 4.5.2。

WCF服务已安装在/ ScgBroadcastorService虚拟路径上,并且已激活net.tcp协议。 (实际上,所有的IIS配置都与我局域网上的个人IIS完全一样)。因此,可以通过以下网址从外部访问该服务:" http://94.23.220.199/ScgBroadcastorService/Service.svc"。

如果您从浏览器访问此链接,您将获得正确的" ScgBroadcastorService服务"带有两个wsdl链接的页面。 (这些链接正确引用" 94.23.220.199" IP地址。

如果单击此链接,则可以正确获取wsdl xml文档。

因为可以从外部访问wsdl文档,所以我希望客户端能够与WCF服务进行通信。

但是,如果我启动客户端,我会收到以下异常:(抱歉,我的家用计算机已本地化为法语...根异常是"服务器拒绝了客户端凭据。")

以下是完整的跟踪:

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)

请注意,如果我直接从主机启动客户端,使用相同的客户端配置文件,客户端将完美地连接和通信!

以下是当前安装在托管服务的服务器上的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>

这是我从外部和主机使用的客户端配置文件:

<?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>

请注意,我添加了

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />

在服务器上的web.config文件末尾行,以获取两个wsdl链接上具有IP地址的服务页面。没有这一行,这两个链接包括计算机名称&#34; ns304385&#34;而不是IP地址,当然无法从外部获取wsdl。

感谢您帮助解决剩余的部署问题。我现在已经陷入困境,并且不知道如何让我的客户端访问我在网络上托管的WCF服务... < / p>

4 个答案:

答案 0 :(得分:3)

好的,我终于在半夜解决了这个问题......

我不得不关闭双方netTcpBinding的安全性。

但要找到如何在服务器端关闭需要双工通信的合同并不是那么简单。

这是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>

诀窍是添加一个安全模式设置为“None”的customTcpBinding,并使用bindingConfiguration属性在端点中引用这个新绑定。

不确定customTcpBinding的所有参数是否最佳,但它们适用于双工合约。 (我的第一次尝试因双工合同而被拒绝)

在客户端,我还必须将绑定的安全模式设置为“None”。这是我在客户端的新配置文件:

<?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>

最后我的WCF Windows WinForm客户端工作正常,并与我的Duplex WCF服务进行通信!

我不得不说解决这个问题是一场真正的噩梦......

希望这个问题的完整描述将有助于其他开发人员尝试托管他们的双工网络.Tcp绑定网络上的WCF服务,并在找到修改配置文件的正确方法之前厌倦了所有意外的异常。

晚安伙计们!回到工作岗位前只有三个小时的睡眠时间......: - (

答案 1 :(得分:0)

将安全模式设置为“none”不是解决此问题的方法,因为将安全性设置为None也会删除邮件的机密性(加密)和完整性(签名)。

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

要删除身份验证,您可以: 1)将安全模式设置为消息

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

2)或将安全模式设置为Transport

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

我必须承认我没有尝试使用您的代码。 只是想确保阅读此内容的人确实知道身份验证和邮件安全性不是一回事,并指出它们是正确的方向。

答案 2 :(得分:0)

我在远程计算机上的Windows服务中托管的双工wcf服务遇到了同样的错误。为了使其工作,我必须创建入站和出站规则以在Windows防火墙高级设置中打开服务端口。我还创建了一个服务器用户,并将此代码包含在我的客户端中:

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

答案 3 :(得分:-1)

哇!哇!我刚刚找到了部分解决方案!

至少解释为什么我无法从外部到达WCF服务!

阅读本文: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

因此,当我尝试启动客户端时,我尝试使用与我在家用计算机上使用的帐户相同的名称和密码在服务器上创建一个帐户......并且...它有效!!! / p>

当然你会同意我这是不可接受的......

我希望我的WCF服务能够接受来自网上任何地方的用户的客户联系,并使用他们自己的用户名/密码,我不想关心!

所以,搜索继续以另一种方式...如何允许WCF客户端连接而不必在我的服务器上创建帐户?

敬请关注......希望很快就能找到答案......