.NET Remoting 體系結構 之 信道的功能和配置 (一)
- 2020 年 2 月 17 日
- 筆記
信道
信道用於.NET 客戶端和伺服器之間的通訊。.NET Framework 4 發布的信道類使用 TCP 、HTTP 或IPC 進行通訊。我們可以為其他的協議創建自定義信道。 HTTP 信道使用 HTTP 協議進行通訊。因為防火牆通常讓埠 80 處於打開的狀態,所以客戶端能 夠訪問 Web 伺服器,因為.NET Remoting Web 服務可以偵聽埠 80,所以客戶端更容易使用它們。 雖然在 Internet 上也可以使用 TCP 信道,但是必須配置防火牆,這樣客戶端能夠訪問 TCP 信道 所使用的指定埠。與 HTTP 信道相比,在內部網環境中使用 TCP 信道能夠進行更加高效的通訊。 IPC 信道適合於在單個系統上進行跨進程的通訊。因為它使用 Windows 進程間通訊機制,所 以它比其他信道快。當執行遠程對象上的方法調用時,導致客戶信道對象就把消息發送到遠程信道對象中。 伺服器應用程式和客戶端應用程式都必須創建信道。 下面的程式碼說明了如何在伺服器端創建 TcpServerChannel:
using System.Runtime.Remoting.Channels.Tcp; //省略... TcpServerChannel channel = new TcpServerChannel(8086);
構造函數的參數指定 TCP 套接字偵聽哪個埠。伺服器信道必須指定一個眾所周知的埠,在 訪問伺服器時,客戶端必須使用該埠。但是,在客戶端上創建 TcpClientChannel 時,不必指定一 個眾所周知的埠,TcpClientChannel 的默認構造函數會選擇一個可用埠,在客戶端與伺服器連接 時,該埠被傳遞給伺服器,以便伺服器能夠把數據返回給客戶端。 創建新的信道實例,會使套接字立即轉換到偵聽狀態,在命令行中輸入 netstat –a,可以驗證套 接字是否處於偵聽狀態。
HTTP 信道的使用方式類似於 TCP 信道。可以指定伺服器能在哪個埠上創建偵聽套接字。 伺服器可以偵聽多個信道。下面的程式碼創建並註冊 HTTP、TCP 和IPC 信道:
var tcpChannel = new TcpServerChannel(8086); var httpChannel = new HttpServerChannel(8085); var ipcChannel = new IpcServerChannel("myIPCPort"); // register the channels ChannelServices.RegisterChannel(tcpChannel, true); ChannelServices.RegisterChannel(httpChannel, false); ChannelServices.RegisterChannel(ipcChannel, true);
信道類必須實現 IChannel 介面。IChannel 介面有以下兩個屬性:
● ChannelName 屬性是只讀的,它返回信道的名稱。信道的名稱取決於協議的類型,例如, HTTP 信道的名稱為 HTTP。
● ChannelPriority 屬性也是只讀的。在客戶端和伺服器之間可以使用多個信道進行通訊,優先 級定義信道的次序。在客戶端上,具有較高優先順序的信道首先連接到伺服器上。優先順序值 越高,優先順序就越高,其默認值是 1,但允許使用負值創建較低的優先順序。
實現的其他介面要根據信道的類型決定,伺服器信道實現 IChannelReceiver 介面,而客戶端信 道實現 IChannelSender 介面。 HTTPChannel、TcpChannel 和 IPCChannel 類都可以用於伺服器和客戶端。它們實現 IChannelSender 和 IChannelReceiver 介面。這些介面都派生自 IChannel 介面。 客戶端的 IchannelSender 介面除了有 Ichannel 介面之外,還有一個 CreateMessageSink()方法,這 個方法返回一個實現 IMessageSink 介面的對象。IMessageSink 介面可以把同步和非同步消息放到信道 中。在使用伺服器端的介面 IChannelReceiver 時,通過 StartListening()方法可以把信道設置為偵聽狀 態,而通過 StopListening()方法則可以停止對信道的偵聽。ChannelData 屬性用於訪問所獲取的數據。
使用信道類的屬性,可以獲取信道的配置資訊。HTTP和TCP 信道都有 ChannelName、ChannelPriority 和ChannelData 屬性。使用 ChannelData 屬性可以獲取存儲在 ChannelDataStore 類中的 URI 資訊。此外, HttpServerChannel 類還有一個 Scheme 屬性。下面的程式碼顯示一個輔助方法 ShowChannelProperties(),該 方法在文件中顯示對應的資訊:
1 static void ShowChannelProperties(IChannelReceiver channel) 2 { 3 Console.WriteLine("Name: {0}", channel.ChannelName); 4 Console.WriteLine("Priority: {0}", channel.ChannelPriority); 5 if (channel is TcpChannel) 6 { 7 TcpChannel tcpChannel = channel as TcpChannel; 8 Console.WriteLine("is secured: {0}", tcpChannel.IsSecured); 9 } 10 11 if (channel is HttpServerChannel) 12 { 13 HttpServerChannel httpChannel = channel as HttpServerChannel; 14 Console.WriteLine("Scheme: {0}", httpChannel.ChannelScheme); 15 } 16 17 ChannelDataStore data = (ChannelDataStore)channel.ChannelData; 18 if (data != null) 19 { 20 foreach (string uri in data.ChannelUris) 21 { 22 Console.WriteLine("URI: " + uri); 23 } 24 } 25 Console.WriteLine(); 26 }
設置信道屬性 使用構造函數 TCPServerChannel(IDictionary,IServerChannelSinkProvider),可以在一個列表中設 置信道的所有屬性。Dictionary 泛型類實現 IDictionary 介面,因此,使用這個類可以設置 Name、 Priority 和 Port 屬性。為了使用 Dictionary 類,必須導入 System.Collections.Generic 名稱空間。 在 TcpServerChannel 類的構造函數中,除了參數 IDictionary 之外,還可以傳遞實現 IServerChannelSinkProvider 介面的對象。在本例中,設置 BinaryServerFormatterSinkProvider 而不設置 SoapServerFormatterSinkProvider,前者是 HttpServerChannel 的默認值。BinaryServerFormatterSinkProvider 類的默認實現程式碼把 BinaryServerFormatterSink 類與使用 BinaryFormatter 對象的信道聯繫起來,以轉換 數據,便於傳輸:
1 var properties = new Dictionary<string, string>(); 2 properties["name"] = "HTTP Channel with a Binary Formatter"; 3 properties["priority"] = "15"; 4 properties["port"] = "8085"; 5 var sinkProvider = new BinaryServerFormatterSinkProvider(); 6 var httpChannel = new HttpServerChannel(properties, sinkProvider);
根據信道的類型,可以指定不同的屬性。TCP 和HTTP 信道都支援本例中使用的 name 和priority 信道屬性。這些信道也支援其他屬性,如 bindTo,如果電腦配置了多個 IP 地址,bindTo 指定綁定可以使用的 IP 地址。TCP 伺服器信道支援 rejectRemoteRequests,只允許從本地電腦上連接客 戶端。
信道的「可插入性」
創建的自定義信道可以使用 HTTP、TCP 和 IPC 之外的其他傳輸協議發送消息。此外,也可以 對現有的信道進行擴展,從而提供更多功能:
● 發送部分必須實現 IChannelSender 介面。重要的部分是 CreateMessageSink()方法,在該方 法中,客戶端要發送 URL,此外,使用這個方法可以實例化與伺服器的連接。在這裡必須 創建消息接收器,代理使用該消息接收器把消息發送到信道中。
● 接收部分必須實現 IChannelReceiver 介面。必須在 ChannelData 的get 屬性中啟動偵聽功能。 然後,可以等待另一個執行緒接收來自客戶端的數據。在打亂消息之後,使用 ChannelServices. SyncDispatchMessage()方法把消息分配給對象。