繁体   English   中英

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

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

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

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

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

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

如果您从浏览器访问此链接,您将获得一个带有两个wsdl链接的正确“ScgBroadcastorService服务”页面。 (这些链接正确地引用“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地址的服务页面。 没有这一行,这两个链接包括计算机名“ns304385”而不是IP地址,当然wsdl不能从外部获取。

感谢您帮助我解决剩余的部署问题。我现在陷入困境,不知道如何让我的客户端到达我在网络上托管的WCF服务...

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

我不得不关闭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服务,并在找到正确的修改配置文件的方法之前厌倦了所有意外的异常。

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

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

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

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

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

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

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

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

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

至少解释为什么我无法从外面到达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

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

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

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

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

请继续关注...希望很快能找到答案......

暂无
暂无

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

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