WCF後傳系列(9):深度通道編程模型Part 2—實例篇

引言

從本質上說,WCF是一個通信服務框架,它允許我們使用不同的傳輸協議,使用不同的消息編碼形式,跟不同的WS-*系列規範交互,而所有這些細節都是由通道堆棧來處理的。在《WCF專題系列(8):深度通道編程模型Part 1—設計篇》中,對於WCF中的通道模型有了深入的認識,本文中,我將通過實例來說明在通道模型中,服務端是如何接收消息,客戶端是如何發送消息的。

服務端通道

本文將不使用WCF的編程模型,而直接利用通道模型來進行通信,這樣有助於我們更進一步加深對服務端處理消息的認識,在服務端偵聽並接收消息的第一步需要創建綁定,我們既可以使用WCF中內置的綁定或者使用自定義的綁定,如下代碼所示,創建一個CustomBinding:
// 創建自定義綁定
BindingElement[] bindingElements = new BindingElement[2];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElements);
此處添加了HttpTransportBindingElement,所以生成的通道堆棧具有HTTP傳輸通道,另外採用了文本消息編碼器。接下來調用剛纔創建的CustomBinding的BuildChannelListener方法來構造通道偵聽器,需要指定偵聽基地址以及綁定參數,另外調用Open()方法打開通道監聽器,相信大家一定還記得Open()方法是在接口ICommunicationObject中定義的,如下代碼所示:
// 使用自定義綁定創建通道偵聽器         
IChannelListener<IReplyChannel> listener =
      binding.BuildChannelListener<IReplyChannel>(
         new Uri("http://localhost:8887/StringService"),
         new BindingParameterCollection());
// 監聽消息
listener.Open();
Console.WriteLine("Listening for incoming channel connections");
現在偵聽傳入的消息,由於我們使用請求了響應消息交換模式,此處偵聽器返回一個實現了IReplyChannel的通道,爲了在此通道上接收消息,我們首先對其調用Open()方法(該方法仍然是在ICommunicationObject中定義),以便將其置於一個準備進行通信的狀態。 然後,我們調用ReceiveRequest()方法,它會處於阻止狀態,直到消息達到,如下代碼所示:
// 創建Reply通道
IReplyChannel channel = listener.AcceptChannel();
Console.WriteLine("Channel accepted. Listening for messages");
channel.Open();
RequestContext request = channel.ReceiveRequest();
當ReceiveRequest()方法返回一個RequestContext時,再使用其RequestMessage屬性獲取接收到的消息。輸出消息的操作(Action)和內容。爲了發送答覆,在此例中創建一個新的答覆消息,它會將我們在請求中接收到的字符串數據,添加一段字符後再傳遞回去。然後,調用Reply()方法以發送答覆消息,如下代碼所示:
// 讀取請求的消息
Message message = request.RequestMessage;
Console.WriteLine("Message Received");
Console.WriteLine("Message Action: {0}", message.Headers.Action);
string body = message.GetBody<string>();
Console.WriteLine("Message Content: {0}", body);
// 發送響應消息
Message replymessage = Message.CreateMessage(
    binding.MessageVersion,
    "http://www.cnblogs.com/TerryLee/Encode",
     "Hello : " + body);
request.Reply(replymessage);
最後別忘了做資源釋放工作,關閉通道偵聽器、通道、請求消息、請求上下文等,如下代碼所示:
// 釋放對象
message.Close();
request.Close();
channel.Close();
listener.Close();
完整的代碼如下所示:
/// <summary>
/// Author:TerryLee
/// Url:[url]http://www.cnblogs.com/terrylee[/url]
/// </summary>
static void Main()
{
    // 創建自定義綁定
    BindingElement[] bindingElements = new BindingElement[2];
    bindingElements[0] = new TextMessageEncodingBindingElement();
    bindingElements[1] = new HttpTransportBindingElement();
    CustomBinding binding = new CustomBinding(bindingElements);
    // 使用自定義綁定創建通道偵聽器         
    IChannelListener<IReplyChannel> listener =
          binding.BuildChannelListener<IReplyChannel>(
             new Uri("http://localhost:8887/StringService"),
             new BindingParameterCollection());
    // 監聽消息
    listener.Open();
    Console.WriteLine("Listening for incoming channel connections");
    // 創建Reply通道
    IReplyChannel channel = listener.AcceptChannel();
    Console.WriteLine("Channel accepted. Listening for messages");
    channel.Open();
    RequestContext request = channel.ReceiveRequest();
    // 讀取請求的消息
    Message message = request.RequestMessage;
    Console.WriteLine("Message Received");
    Console.WriteLine("Message Action: {0}", message.Headers.Action);
    string body = message.GetBody<string>();
    Console.WriteLine("Message Content: {0}", body);
    // 發送響應消息
    Message replymessage = Message.CreateMessage(
        binding.MessageVersion,
        "http://www.cnblogs.com/TerryLee/Encode",
         "Hello : " + body);
    request.Reply(replymessage);
    // 釋放對象
    message.Close();
    request.Close();
    channel.Close();
    listener.Close();
    Console.WriteLine("Press Enter to exit");
    Console.ReadLine();
}
現在運行服務端如圖1所示,由於沒有消息到達,所以ReceiveRequest()方法會阻塞:
TerryLee_WCF_31
圖 1

客戶端通道

前面完成了服務端的工作,接下來我們看看如何在客戶端直接使用通道模型進行通信。與服務端一致,請求消息的第一步是創建綁定,因爲雙方需要通過綁定就通信的細節達成一致。創建自定義綁定與服務端一致,如下代碼所示:
// 創建綁定
BindingElement[] bindingElements = new BindingElement[2];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElements);
接下來需要使用剛纔創建的綁定來構造通道工廠,在上一篇中我們提到,消息的接收方使用通道偵聽器,而消息的請求方使用通道工廠,這次使用BuildChannelFactory()方法構造通道工廠並打開,如下代碼所示:
// 使用綁定創建通道工廠
IChannelFactory<IRequestChannel> factory =
binding.BuildChannelFactory<IRequestChannel>(
                 new BindingParameterCollection());
// 打開通道工廠
factory.Open();
Console.WriteLine("Channel factory opened");
現在使用通道工廠的CreateChannel()方法來創建IRequestChannel,得到通道後,調用它的Open()方法以使其處於通信就緒狀態,如下代碼所示:
// 創建Request通道
IRequestChannel channel = factory.CreateChannel(
   new EndpointAddress("http://localhost:8887/StringService"));
channel.Open();
Console.WriteLine("Request channel opened");
打開通道之後,就可以創建消息並使用通道的 Request()方法發送請求並等待響應,這裏我們發送的消息內容是“TerryLee”,當此方法返回時,我們將能夠得到回覆消息,可以讀取該消息以發現終結點回復的內容,如下代碼所示:
// 創建請求消息
Message requestmessage = Message.CreateMessage(
    binding.MessageVersion,
    "http://www.cnblogs.com/TerryLee/Encode",
     "TerryLee");
// 發送請求消息並接收響應消息
Message replymessage = channel.Request(requestmessage);
Console.WriteLine("Reply message received");
Console.WriteLine("Reply action: {0}",
                      replymessage.Headers.Action);
string data = replymessage.GetBody<string>();
Console.WriteLine("Reply content: {0}", data);
最後仍然是資源釋放工作,關閉通道工廠、通道以及請求消息,如下代碼所示:
replymessage.Close();
channel.Close();
factory.Close();
完整的客戶端代碼爲:
/// <summary>
/// Author:TerryLee
/// Url:[url]http://www.cnblogs.com/terrylee[/url]
/// </summary>
public static void Main()
{
    // 創建綁定
    BindingElement[] bindingElements = new BindingElement[2];
    bindingElements[0] = new TextMessageEncodingBindingElement();
    bindingElements[1] = new HttpTransportBindingElement();
    CustomBinding binding = new CustomBinding(bindingElements);
    // 使用綁定創建通道工廠
    IChannelFactory<IRequestChannel> factory =
    binding.BuildChannelFactory<IRequestChannel>(
                     new BindingParameterCollection());
    // 打開通道工廠
    factory.Open();
    Console.WriteLine("Channel factory opened");
    // 創建Request通道
    IRequestChannel channel = factory.CreateChannel(
       new EndpointAddress("http://localhost:8887/StringService"));
    channel.Open();
    Console.WriteLine("Request channel opened");
    // 創建請求消息
    Message requestmessage = Message.CreateMessage(
        binding.MessageVersion,
        "http://www.cnblogs.com/TerryLee/Encode",
         "TerryLee");
    // 發送請求消息並接收響應消息
    Message replymessage = channel.Request(requestmessage);
    Console.WriteLine("Reply message received");
    Console.WriteLine("Reply action: {0}",
                          replymessage.Headers.Action);
    string data = replymessage.GetBody<string>();
    Console.WriteLine("Reply content: {0}", data);
    replymessage.Close();
    channel.Close();
    factory.Close();
    Console.WriteLine("Press Enter to exit");
    Console.ReadLine();
}
最後運行時服務端如圖2所示:
TerryLee_WCF_33 
圖 2
客戶端如圖3所示:
TerryLee_WCF_32
圖 3

溫故知新

現在我們再回顧一下上一篇中所講的知識,通道對象模型是實現通道、通道偵聽器和通道工廠所必需的一組核心接口。還提供一些基類以輔助自定義實現。可以看到通道模型中最重要的有三組接口:通道、通道偵聽器和通道工廠。每個通道均實現一個或多個接口,稱爲通道形狀接口或通道形狀;通道偵聽器負責偵聽傳入消息,即在消息的接收端,然後通過由通道偵聽器創建的通道將這些消息傳送到上面的層;通道工廠負責創建通道用於發送消息,即在消息的發送方,並在通道工廠關閉時,關閉通道工廠創建的所有通道。如圖4所示:
TerryLee_WCF_24
圖 4
對照本文的示例代碼,相信大家對於圖4能夠有更深的認識。

0

收藏

lihuijun

203篇文章,65W+人氣,0粉絲

Ctrl+Enter 發佈

發佈

取消

掃一掃,領取大禮包

0

分享
lihuijun
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章