接收和接收鏈結
通道會在傳送訊息之前或接收訊息之後,沿著通道接收物件鏈結傳送每個訊息。這個接收鏈結包含基本通道功能所需的接收 (例如格式子、傳輸或 stackbuilder 接收),但是您可自訂通道接收鏈結以執行訊息或資料流的特殊工作。
每個通道接收都會實作 IClientChannelSink 或 IServerChannelSink。用戶端上的第一個通道接收還必須實作 IMessageSink。它通常會實作 IClientFormatterSink (其繼承自 IMessageSink、IChannelSinkBase 和 IClientChannelSink),而因為它會將內送訊息轉換成資料流 (IMessage 物件),所以又稱為格式子接收。
通道接收鏈結會處理任何送往或來自應用程式定義域的訊息。此時您只有這個訊息,但是可對該訊息做任意處理,而後續處理會使用您在處理後傳回給系統的訊息。此處可用來在用戶端或伺服器上實作登入服務、任意篩選條件或甚至是加密或其他安全性措施。下圖顯示基本通道接收鏈結的結構。
基本通道接收鏈結
請注意,每個通道接收都會處理資料流,然後將該資料流傳遞到下個通道接收,這表示接收前後的物件都應知道如何處理已傳遞給它們的資料流。
![]() |
---|
訊息接收不得擲回例外狀況。訊息接收可控制這個情況的一種方法是將方法程式碼放在 try-catch 區塊中。 |
通道接收提供者 (實作 IClientChannelSinkProvider、IClientFormatterSinkProvider 或 IServerChannelSinkProvider 介面的物件) 負責建立遠端處理訊息所流過的通道接收。啟動遠端型別時,會從通道中擷取通道接收提供者;接著會在接收提供者上呼叫 CreateSink 方法,以擷取鏈結中的第一個通道接收。
通道接收負責在用戶端與伺服器間傳輸訊息。通道接收間也會連結成一條鏈結。在接收提供者上呼叫 CreateSink 方法時,該方法應會執行下列動作:
建立它自己的通道接收。
在鏈結中的下個接收提供者上呼叫 CreateSink。
確定下個接收和目前的接收已連結在一起。
將它的接收傳回給呼叫端。
通道接收負責將在其上進行的所有呼叫轉送給鏈結中的下個接收,因此應該會提供儲存下個接收之參考的機制。
通道接收對於其沿著接收鏈結傳送的內容具有很大的彈性。例如,想要在傳送實際序列化的原始訊息之前協調驗證的安全性接收,可保留完整通道訊息、將內容資料流取代為它們自己的內容,並將它沿著接收鏈結傳送給遠端應用程式定義域。在傳回的路程中,安全性接收可攔截回覆訊息,而與遠端應用程式定義域中對應的安全性接收建立交談。一旦達成協議後,原始的安全性接收可將原始內容資料流傳送給遠端應用程式定義域。
通道接收鏈結中的訊息處理
一旦 .NET 遠端處理系統找到可處理 IMethodCallMessage 實作的通道後,該通道就會呼叫 IMessageSink.SyncProcessMessage (或 IMessageSink.AsyncProcessMessage),將訊息傳遞給格式子通道接收。格式子接收會建立傳輸標頭陣列,並在下個接收上呼叫 IClientChannelSink.GetRequestStream。這個呼叫會沿著接收鏈結轉送,而任何接收都可建立要傳回給格式子接收的要求資料流。如果 GetRequestStream 傳回 Null 參考 (在 Visual Basic 中為 Nothing),則格式子接收會建立它自己的接收,以用來進行序列化。一旦傳回這個呼叫後,就會序列化該訊息,並在接收鏈結的第一個通道接收上呼叫適當的訊息處理方法。
接收無法將資料寫入至資料流中,但可讀取資料流或在需要時傳遞新的資料流。接收也可將標頭加入至標頭陣列 (如果它們之前未在下個接收上呼叫 GetRequestStream ),並在將呼叫轉送給下個接收之前,先將它們自己加入至接收堆疊。呼叫到達鏈結結尾的傳輸接收時,傳輸接收會透過通道將標頭和序列化訊息傳送給伺服器,由該伺服器反向執行整個處理序。伺服器端的傳輸接收會從資料流的伺服器端擷取標頭和序列化訊息,並透過接收鏈結轉送這些項目,直到到達格式子接收為止。格式子接收會將訊息還原序列化,並將它轉送給遠端處理系統,由這個系統將訊息還原為方法呼叫,並在伺服器物件上叫用。
建立通道接收鏈結
若要建立新的通道接收,您必須實作和設定遠端處理系統以辨識 IServerChannelSinkProvider 或 IClientChannelSinkProvider 實作,該實作可建立您自訂的 IClientChannelSink 或 IServerChannelSink 實作,或擷取鏈結中的下個接收。可使用 BaseChannelSinkWithProperties 抽象類別,協助實作您的自訂通道接收。
建置通道接收提供者
應用程式可提供伺服器或用戶端通道接收提供者,以做為建構通道時的參數。通道接收提供者應儲存在鏈結中,而使用者在將外層通道接收提供者傳遞給通道建構函式之前,必須負責將所有通道接收提供者鏈結在一起。針對這個目的,通道接收提供者會實作 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
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:
將呼叫 (以訊息表示) 轉換成資料流,並透過 Wire 傳送的處理期間。
從 Wire 取下資料流並將之傳送至 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
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());
}
}
如需在網際網路資訊服務 (IIS) 中裝載這個通道和格式子組合的完整範例,請參閱 遠端處理範例:在網際網路資訊服務 (IIS) 中進行裝載。
若要將這個用戶端變更為搭配使用 TcpChannel 物件和 SoapClientFormatterSink 物件,則只需要變更命名空間和 RegisterChannel 呼叫,如下列程式碼範例所示。
ChannelServices.RegisterChannel(New TcpChannel(properties, New SoapClientFormatterSinkProvider(), Nothing))
ChannelServices.RegisterChannel(new TcpChannel(null, new SoapClientFormatterSinkProvider(), null));
請參閱
概念
在網際網路資訊服務 (IIS) 中裝載遠端物件
遠端處理範例:在網際網路資訊服務 (IIS) 中進行裝載