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