程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WCF後續之旅(14):TCP端口共享

WCF後續之旅(14):TCP端口共享

編輯:關於.NET

基於TCP/IP協議簇的對等網絡通信下,相互通信的應用程序運行各自的進程中,出於應用層的進程將數據局封裝成數據報,並通過傳輸層的TCP或者UDP進行網絡通信。而TCP和UPD則通過一個16bit的端口來識別不同的應用程序。

對於一些常用網絡服務,他們都有一個知名的端口好與之匹配。比如,FTP服務是用的TCP端口為21;Telnet服務的TCP端口為23等等。而對於客戶端通常對所使用的端口並不關心,只需要保證端口在本機是唯一的就可以了,這樣的端口又成為臨時端口,臨時端口一般在1024到5000之間。

一般來講,在某一個時刻,一個端口只能供一個應用程序使用。對於WCF來說,當我們通過一個托管的應用程序對某個服務進行寄宿的時候,一個端口被該應用程序獨占使用。如何多個寄宿進行使用相同的端口

在下面的例子中,我通過兩個不同的控制台應用程序對兩個服務,Service1和Service2進行寄宿,兩個服務的終結點地址共享相同的端口:9999。

using(ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
{
    serviceHost.AddServiceEndpoint(typeof(IService1), new NetTcpBinding(), "net.tcp://127.0.0.1:9999/service1");
    serviceHost.Open();
    Console.Read();
}
using(ServiceHost serviceHost = new ServiceHost(typeof(Service2)))
{
    serviceHost.AddServiceEndpoint(typeof(IService2), new NetTcpBinding(), "net.tcp://127.0.0.1:9999/service2");
    serviceHost.Open();
    Console.Read();
}

當我們先後運行這兩個服務寄宿應用程序,第一個能夠正常運行,但是對於第二個,則會拋出如下一個AdressAlreadyInUseException異常,錯誤信息為:

IP 終結點 127.0.0.1:9999 上已有偵聽器。請確保未在應用程序中多次嘗試使用該終結點,並確保沒有其他應用程序在偵聽該終結點。

在本節中,我們將介紹如何解決這種端口被某一個應用程序獨占使用的問題,讓不同的監聽程序能夠共享同一個端口。在這之前,我們需要了解一下,端口的共享具有什麼現實的意義。

1. 端口共享在WCF中的意義何在?

在一般的網絡環境中,盡可能避免網絡攻擊,都會通過防火牆將絕大部分的端口封掉,僅僅保留那些常用的網絡服務所用的端口,或者為某一個類應用保留少量的端口。總而言之,我們不能保證每個跨防火牆通信的應用都具有一個唯一的端口,他們只能共享一個或者少量的幾個端口。

對於Intranet內部,為了保證部署於局域網內的其他計算機的網絡應用能夠與本機進行正常通信,通常會在本機的防火牆中預留一個可用的端口。Intranet內部的主機之間可以使用這些預留的端口通過相應的傳輸協議,比如TCP、HTTP、Named Pipe等等,進行通信。而對於處於Internet和本地網絡之間的防火牆,通常僅僅只有保留80端口,保證基於HTTP的網絡通信能夠正常進行。

所以,無論是基於Intranet還是Internet,無論是采用何種傳輸協議,端口共享——讓多個網絡應用程序使用相同的端口進行通信,都具有重要的現實意義。

對於WCF來講,當我們將某個服務寄宿於一個進程中,實際上就是通過該進程監聽和處理來自客戶端的Socket請求。在一般情況下,一個端口被一個監聽進行獨占使用,也就是說,如何你的主機上部署了若干服務,而這些服務寄宿於不同的應用程序中,對於這種寄宿應用程序來說,監聽的端口必須不同。

所以,我們需要通過特殊的途徑實現基於WCF寄宿的端口共享。對於采用不同的傳輸協議,我們有不同的解決方案,對於HTTP協議,我們可以通過IIS的寄宿方式實現端口的共享,對於TCP,.NET Framework3.0提供了一個特殊的Windows服務,Net.TCP Port Sharing Service,幫助我們輕松的實現端口的共享。我們接下來就討論這種端口共享解決方案。

2、Net.TCP Port Sharing Service

從功能上講,Net.TCP Port Sharing Service實現了和HTTP.SYS相同的功能:請求的監聽和分發(request listening and dispatching)。唯一不同是,HTTP.SYS運行在內核模式(Kernel Mode)下,而Net.TCP Port Sharing Service運行在用戶模式(User Mode)下。

WCF對Net.TCP Port Sharing Service提供了原生的支持。Net.TCP Port Sharing Service在WCF的實現原理如下圖所示:在Net.TCP Port Sharing Service開啟的狀態下,如果我們通過兩個服務寄宿應用程序分別寄宿兩個服務,Service1和Service2,並且它們共享一樣的監聽端口:8888。實際上,當ServiceHost的Open方法被執行的時候,WCF會將這兩個地址,net.tcp://artech.com:8888/service1和net.tcp://artech.com:8888/service2注冊到Net.TCP Port Sharing Service中。而對於Net.TCP Port Sharing Service來說,在其內部維護者一個目的地址和進程的列表,在進行目的地址注冊的時候,會將這兩個地址和對應的服務寄宿地址的匹配關系添加到該列表之中。

當我們的服務客戶端,proxy1和proxy2,分別調用service1和service2。當基於他們各自服務調用的socket連接請求抵達artech.com的時候,Net.TCP Port Sharing Service會截獲請求消息,並獲取目的地址。根據該目的地址,結合內部維護的目的地址和目標進程匹配列表,Net.TCP Port Sharing Service得到對應的目標應用程序,並將請求消息向真正的目標程序進行轉發。

3 、基於TCP端口共享的編程

由於WCF下基於TCP的端口共享是建立在Net.TCP Port Sharing Service Windows服務上的。所有安裝有.NET Framework3.0的操作系統都具有該Windows服務,但是在默認的情況下,該服務是不可用的。當你第一次使用Net.TCP Port Sharing Service,或者發現該服務被禁用,你需要手工的啟用該服務。

注:通過“開始”-〉“控制面板”-〉“管理工具”-〉服務,打開如下圖所示的“服務對話框”,然後定位到Net.TCP Port Sharing Service。

在基於TCP的WCF通信中,我們使用NetTcpBinding處理通信的所有細節,這些細節中也包括端口的共享。為此在NetTcpBinding中,定義了一個特殊的屬性,PortSharingEnabled,表明是否啟動端口共享機制。

public class NetTcpBinding : Binding, IBindingRuntimePreferences
{
    ... ...
   public bool PortSharingEnabled { get; set; }
}

如何我們以代碼的方式進行服務的寄宿的話,我們僅僅需要將終結點的NetTcpBinding上將該屬性設為True就可以了:

using(ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
{
   NetTcpBinding binding = new NetTcpBinding();
   binding.PortSharingEnabled = true;
   serviceHost.AddServiceEndpoint(typeof(IService1),binding,"net.tcp://127.0.0.1:9999/service1");
   serviceHost.Open();
   Console.Read();
}

當然,你也通過通過培植的方式來指定NetTcpBinding的PortSharingEnabled屬性:

<configuration>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="portSharingBinding" portSharingEnabled="true" />
      </netTcpBinding>
    </bindings>
    <services>
      <service name="Artech.WcfServices.Services.CalculateService">
        <endpoint binding="netTcpBinding"  bindingConfiguration="portSharingBinding"
          contract="Artech.WcfServices.Contracts.ICalculate" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved