服務通道層級的程式設計
本主題說明如何撰寫 Windows Communication Foundation (WCF) 服務應用程式,而不使用 System.ServiceModel.ServiceHost 類別及其相關的物件模型。
接收訊息
以下為準備接收和處理訊息時所需的步驟:
建立繫結。
建置通道接聽程式。
開啟通道接聽程式。
讀取要求並傳送回覆。
關閉所有通道物件。
建立繫結。
接聽與接收訊息的第一步,就是建立繫結。WCF 隨附幾個內建或系統提供的繫結,您可以產生其中一項來直接使用。此外,您也可以產生 CustomBinding 類別來建立自己的自訂繫結 (清單 1 中的程式碼也會執行相同作業)。
下列的程式碼範例會建立 System.ServiceModel.Channels.CustomBinding 的執行個體,並將 System.ServiceModel.Channels.HttpTransportBindingElement 新增至其項目集合 (用來建置通道堆疊的繫結項目集合)。在此範例中,由於項目集合只具有 HttpTransportBindingElement,因此結果通道堆疊也只有 HTTP 傳輸通道。
建置 ChannelListener
在建立繫結後,我們可以呼叫 System.ServiceModel.Channels.Binding.BuildChannelListener.Uri,System.ServiceModel.Channels.BindingParameterCollection) 來建置通道接聽程式,其中的型別參數就是要建立的通道類型。在此範例中,我們會使用 System.ServiceModel.Channels.IReplyChannel,因為我們想要以要求/回覆訊息交換模式來接聽傳入訊息。
IReplyChannel 會被用來接收要求訊息與傳回回覆訊息。呼叫 System.ServiceModel.Channels.IReplyChannel.ReceiveRequest 會傳回 System.ServiceModel.Channels.IRequestChannel,以便用來接收要求訊息並傳回回覆訊息。
在建立接聽程式時,我們會傳送接聽程式所接聽的網路位址,在此範例為 https://localhost:8080/channelapp
。一般來說,每個傳輸通道都支援一到數個位址配置,例如,HTTP 傳輸同時支援 http 和 https 配置。
在建立接聽程式時,我們同時會傳送空的 System.ServiceModel.Channels.BindingParameterCollection。繫結參數機制會負責將控制接聽程式建置方式的參數傳送出去。我們的範例並未使用此類參數,因此我們會傳送空的集合。
接聽傳入訊息
接著,我們會在接聽程式上呼叫 System.ServiceModel.ICommunicationObject.Open 並開始接受通道。System.ServiceModel.Channels.IChannelListener.AcceptChannel 的行為將視傳輸為連線導向還是沒有連線模式而定。如果是連線導向傳輸,AcceptChannel 會在新的連線要求進入時才停止封鎖,這時它會傳回一個代表該項新連線的新通道。如果是沒有連線的傳輸,例如 HTTP,則 AcceptChannel 會立即傳回由傳輸接聽程式所建立,且是唯一的通道。
在此範例中,接聽程式會傳回可實作 IReplyChannel 的通道。為了在此通道上接收訊息,首先我們必須在其上呼叫 System.ServiceModel.ICommunicationObject.Open,以將之轉換成準備通訊的狀態。接著我們會呼叫 ReceiveRequest 並在訊息抵達之前進行封鎖。
讀取要求並傳送回覆
當 ReceiveRequest 傳回 RequestContext,我們會透過其 RequestMessage 屬性來取回接收的訊息。我們會寫出訊息的行動與本文內容 (我們假定是字串)。
在此情況下,為了傳送回覆,我們會建立新的回覆訊息並傳回要求中所收到的字串資料。接著,我們會呼叫 Reply 來傳送回覆訊息。
關閉物件
為避免洩漏資源,請在不再需要時,關閉通訊期間所使用的物件,這點請您務必注意。在此範例中,我們會關閉要求訊息、要求內容、通道和接聽程式。
下列程式碼範例示範基本服務,其中的通道接聽程式只會收到一個訊息。真實的服務會在服務結束之前持續接受通道並接收訊息。
using System;
using System.ServiceModel.Channels;
namespace ProgrammingChannels
{
class Service
{
static void RunService()
{
//Step1: Create a custom binding with just TCP.
BindingElement[] bindingElements = new BindingElement[2];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElements);
//Step2: Use the binding to build the channel listener.
IChannelListener<IReplyChannel> listener =
binding.BuildChannelListener<IReplyChannel>(
new Uri("https://localhost:8080/channelapp"),
new BindingParameterCollection());
//Step3: Listening for messages.
listener.Open();
Console.WriteLine(
"Listening for incoming channel connections");
//Wait for and accept incoming connections.
IReplyChannel channel = listener.AcceptChannel();
Console.WriteLine("Channel accepted. Listening for messages");
//Open the accepted channel.
channel.Open();
//Wait for and receive a message from the channel.
RequestContext request= channel.ReceiveRequest();
//Step4: Reading the request message.
Message message = request.RequestMessage;
Console.WriteLine("Message received");
Console.WriteLine("Message action: {0}",
message.Headers.Action);
string data=message.GetBody<string>();
Console.WriteLine("Message content: {0}",data);
//Send a reply.
Message replymessage=Message.CreateMessage(
binding.MessageVersion,
"https://contoso.com/someotheraction",
data);
request.Reply(replymessage);
//Step5: Closing objects.
//Do not forget to close the message.
message.Close();
//Do not forget to close RequestContext.
request.Close();
//Do not forget to close channels.
channel.Close();
//Do not forget to close listeners.
listener.Close();
}
public static void Main()
{
Service.RunService();
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}