通道在傳送訊息之前或接收訊息之後,會沿著通道接收物件鏈結來傳送訊息。這個接收鏈結包含需要基本通道功能的接收,例如格式子、傳輸或堆疊建置器接收,但是您可以自訂通道接收鏈結來執行含有訊息或資料流的特殊工作。
每個通道接收都實作 IClientChannelSink 或 IServerChannelSink。用戶端上的第一個通道接收還必須實作 IMessageSink。它通常實作 IClientFormatterSink (繼承自 IMessageSink、IChannelSinkBase 和 IClientChannelSink) 並稱為格式子接收,因為它會將內送訊息轉換為資料流 (IMessage 物件)。
通道接收鏈結處理往返於應用程式定義域的訊息。此時,您只有訊息,但您能夠隨意處置訊息,接下來的處理將使用您處理過後傳回系統的訊息。在這裏實作記錄服務、任何種類的篩選器,或者是在用戶端或伺服器上進行加密 (Encryption) 或其他安全性措施,都顯得很自然。下圖顯示基本通道接收鏈結的結構。
基本通道接收鏈結
.gif)
請注意到每個通道接收都處理資料流,接著將資料流傳遞至下一個通道接收,這表示在接收之前或之後的物件,應該知道要如何處理您傳遞給它們的資料流。
**注意 **訊息接收絕對不能擲回例生狀況。訊息接收可控制這個問題的方法之一,是將方法程式碼包裝到 Try-Catch 區塊內。
通道接收提供者 (實作 IClientChannelSinkProvider、IClientFormatterSinkProvider 或 IServerChannelSinkProvider 介面的物件) 負責建立遠端處理訊息所流經的通道接收。當啟動遠端型別時,會從通道擷取通道接收提供者;並在接收提供者上呼叫 CreateSink 方法,以便從鏈結擷取接收中的第一通道。
通道接收也負責在用戶端和伺服器之間傳輸訊息。通道接收在鏈結中也是連結在一起。當在接收提供者上呼叫 CreateSink 方法時,它應該執行下列作業:
- 建立自己的通道接收。
- 在鏈結的下一個接收提供者上呼叫 CreateSink。
- 確保下一個接收和目前接收連結在一起。
- 將它的接收傳回呼叫端。
通道接收負責將其上所產生的所有呼叫,都轉寄到鏈結中的下一個接收,並應提供用來儲存下一接收參考的機制。
通道接收對於它們傳送至接收鏈結的內容有極大的彈性。例如,傳送實際序列化的原始訊息之前希望先協商驗證 (Authentication) 的安全性接收,會抓住完整的通道訊息,利用自己的內容來取代內容資料流,並將它傳送至接收鏈結,到遠端應用程式定義域之上。在傳回的途程中,安全性接收可攔截回覆的訊息,並在遠端應用程式定義域中,與對應的安全性接收建立對談。一旦達成協議,起始的安全性接收會將原始內容資料流傳送至遠端應用程式定義域之上。
通道接收鏈結中的訊息處理
一旦 .NET遠端處理系統找到可處理 IMethodCallMessage 實作的通道,該通道便會藉由呼叫 IMessageSink.SyncProcessMessage (或 IMessageSink.AsyncProcessMessage),將訊息傳遞至格式子通道接收。格式子接收建立傳輸的前置資料陣列,並呼叫下一接收上的 IClientChannelSink.GetRequestStream。這個呼叫會轉寄至接收鏈結,且任何接收都可建立將傳遞回格式子接收的要求資料流。如果 GetRequestStream 傳回 Null 參考 (Visual Basic 中的 Nothing),格式子接收會建立它自己的接收以供序列化使用。一旦這個呼叫傳回,就會序列化訊息並在接收鏈內的第一通道接收上呼叫適當的訊息處理方法。
接收無法將資料寫入資料流,但可從資料流中讀取或沿著所要求的位置傳遞新的資料流。接收也會將前置資料加入前置資料陣列 (如果它們之前未呼叫下一個接收上的 GetRequestStream),並在轉寄呼叫至下一個接收之前,將自己加入接收堆疊。當呼叫到達鏈結末端的傳輸接收時,傳輸接收在通道上將前置資料和序列化的訊息傳送至伺服器,整個處理序在此處反轉。傳輸接收 (伺服端上的) 從資料流的伺服端擷取前置資料和序列化的訊息,經由接收鏈結轉寄,直到遇到格式子接收才停止。格式子接收將訊息還原序列化,並轉寄至遠端處理系統,訊息在此處送回至方法呼叫中,並在伺服器物件上接受叫用。
建立通道接收鏈結
若要建立新的通道接收,您必須實作並設定遠端處理系統,以辨識 IServerChannelSinkProvider 或 IClientChannelSinkProvider 實作,這種實作可建立您的自訂 IClientChannelSink 或 IServerChannelSink 實作,或擷取鏈結中的下一個接收。您可以使用 BaseChannelSinkWithProperties 抽象類別 (Abstract Class) 協助實作自訂通道接收。
建置通道接收提供者
在建構通道時,應用程式可將伺服器或用戶端通道接收提供者做為參數。通道接收提供者應儲存在鏈結中,而且在將外部提供者傳遞至通道建構函式之前,使用者應負責將所有通道接收提供者鏈結在一起。通道接收提供者為這個目的實作 Next 屬性。以下程式碼範例說明如何建置用戶端的通道接收提供者。遠端處理範例:通道接收提供者中有完整的範例。
private Function CreateDefaultClientProviderChain() As IClientChannelSinkProvider
Dim chain As New FirstClientFormatterSinkProvider
Dim sink As IClientChannelSinkProvider
sink = chain
sink.Next = New SecondClientFormatterSinkProvider
sink = sink.Next
return chain
End Function
[C#]
private IClientChannelSinkProvider CreateDefaultClientProviderChain(){
IClientChannelSinkProvider chain = new FirstClientFormatterSinkProvider();
IClientChannelSinkProvider sink = chain;
sink.Next = new SecondClientFormatterSinkProvider();
sink = sink.Next;
return chain;
}
**注意 **組態檔中提供多個通道接收提供者時,遠端處理系統會將它們依照在組態檔中找到的順序鏈結在一起。通道接收提供者是在 RemotingConfiguration.Configure() 呼叫期間建立通道時同時建立的。
格式子接收
格式子接收將通道訊息序列化為訊息資料流,就像實作 IMessage 的物件。某些格式子接收實作使用系統所提供的格式子型別 (BinaryFormatter 和 SoapFormatter)。其他實作可使用自己的方法將通道訊轉換為資料流。
格式子接收的函式是用來產生必要的前置資料,並將訊息序列化為資料流。在格式子接收之後,訊息會透過 IMessageSink.ProcessMessage 或 Imessagesink.AsyncProcessMessage 呼叫,轉寄給接收鏈結中的所有接收。在這個階段,訊息已序列化,且僅提供做為資訊。
**注意 **需要建立或修改訊息本身的接收,必須置放在格式子之前的接收鏈結中。只要實作 IClientFormatterSink,很容易可以達成此目標,藉以欺騙系統相信它擁有格式子接收的參考。真正的格式子接收可稍後再放置在接收鏈結中。
在傳回的旅程中,格式子接收將訊息資料流轉換回通道訊息項目 (傳回訊息)。用戶端上的第一個接收必須實作 IClientFormatterSink 介面。當 CreateSink 傳回通道時,傳回的參考轉型為 IClientFormatterSink 型別,所以可以呼叫 IMessage 介面的 SyncProcessMessage。如果轉型失敗,系統會引發例外狀況。
自訂通道接收
在用戶端上,將自訂通道接收插入格式子接收和最後傳輸接收之間的物件鏈結。在用戶端或伺服器通道中插入自訂通道接收,可讓您處理以下兩點的 IMessage:
- 在這個處理序期間,藉由這種方式以訊息表示的呼叫轉換為資料流並在線上傳送。
- 在這個處理序期間,藉由這種方式將資料流從連線移去,並傳送至 StackBuilderSink 物件(伺服器上遠端物件之前的最後訊息接收) 或 Proxy 物件 (在用戶端上)。
自訂接收可讀取或寫入資料 (取決於呼叫是外傳或內送) 至資料流中,並將其他資訊加入至有需要的前置資料中。在這個階段,訊息已由格式子序列化並且無法修改。當訊息呼叫轉寄至鏈結末端的傳輸接收時,傳輸接收利用通道指定的傳輸通訊協定將前置資料寫入資料流,並將資料流轉寄至伺服器上的傳輸接收。
傳輸接收
傳輸接收是用戶端之鏈結中的最後一個接收,也是伺服端之鏈結中的第一個接收。除了傳輸序列化訊息外,傳輸接收還負責將前置資料傳送至伺服器,並在呼叫從伺服器傳回時,擷取前置資料和資料流。這些接收都是建置到通道內,且無法擴充。
取代預設格式子
由於通道是抽象的網路機制,所以您可以設定 .NET 遠端處理系統,結合系統實作通道與所選擇的任何格式子。您可以使用取得以下各項的通道建構函式來執行這個動作:通道屬性的 IDictionary 實作、伺服器端格式子和用戶端格式子。您也可以指定組態檔中的格式子。下列範例指示 .NET 遠端處理組態系統建立 HttpChannel,但在用戶端上使用 BinaryClientFormatterSink。
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<formatter ref="binary"/>
</clientProviders>
<channels>
</application>
</system.runtime.remoting>
</configuration>
以下程式碼以程式設計方式執行相同的動作,其中假設實作 GetServerString 和 GetServerTime 的遠端介面型別 IService。
Imports System
Imports System.Collections
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Public Class ClientProcess
<MTAThread()> _
Public Shared Sub Main()
' Note that any name/value pairs of configuration attributes can be
' placed in this dictionary (the configuration system calls this same
' constructor).
Dim properties As New Hashtable()
properties("name") = "HttpBinary"
ChannelServices.RegisterChannel(New HttpChannel(properties, New BinaryClientFormatterSinkProvider(), Nothing))
' The last parameter above (Nothing) is the server sink provider chain
' to obtain the default behavior (which includes SOAP and
' binary formatters on the server side).
Dim service As IService = CType(Activator.GetObject(GetType(IService), "http://computer:8080/SAService"), IService)
Console.WriteLine("Server string is: " + service.GetServerString())
Console.WriteLine("Server time is: " + service.GetServerTime())
End Sub
End Class
[C#]
using System;
using System.Collections;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
public class ClientProcess{
public static void Main(string[] Args){
// Note that any name/value pairs of configuration attributes can be
// placed in this dictionary (the configuration system calls this
// same HttpChannel constructor).
IDictionary properties = new Hashtable();
properties["name"] = "HttpBinary";
// The last parameter below is the server sink provider chain
// to obtain the default behavior (which includes SOAP and binary
// formatters) on the server side.
ChannelServices.RegisterChannel(new HttpChannel(null, new BinaryClientFormatterSinkProvider(), null));
IService service = (IService)Activator.GetObject(typeof(IService),"http://computer:8080/SAService");
Console.WriteLine("Server string is: " + service.GetServerString());
Console.WriteLine("Server time is: " + service.GetServerTime());
}
}
如需裝載於 Internet Information Services (IIS) 的這個通道和格式子組合的完整範例,請參閱遠端處理範例:在 Internet Information Services (IIS) 中進行裝載。
若要變更這個用戶端以使用 TcpChannel 物件和 SoapClientFormatterSink 物件,您只需要變更命名空間和 RegisterChannel 呼叫,如下列程式碼範例所示。
ChannelServices.RegisterChannel(New TcpChannel(properties, NewSoapClientFormatterSinkProvider(), Nothing))
[C#]
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));
請參閱
進階遠端處理 | 在 Internet Information Services (IIS) 中裝載遠端物件 | 遠端處理範例:在 Internet Information Services (IIS) 中進行裝載