引言
從本質上說,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()方法會阻塞:
圖 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所示:
圖 2
客戶端如圖3所示:
圖 3
溫故知新
現在我們再回顧一下上一篇中所講的知識,通道對象模型是實現通道、通道偵聽器和通道工廠所必需的一組核心接口。還提供一些基類以輔助自定義實現。可以看到通道模型中最重要的有三組接口:通道、通道偵聽器和通道工廠。每個通道均實現一個或多個接口,稱爲通道形狀接口或通道形狀;通道偵聽器負責偵聽傳入消息,即在消息的接收端,然後通過由通道偵聽器創建的通道將這些消息傳送到上面的層;通道工廠負責創建通道用於發送消息,即在消息的發送方,並在通道工廠關閉時,關閉通道工廠創建的所有通道。如圖4所示:
圖 4
對照本文的示例代碼,相信大家對於圖4能夠有更深的認識。
©著作權歸作者所有:來自51CTO博客作者lihuijun的原創作品,如需轉載,請註明出處,否則將追究法律責任
0
收藏
Ctrl+Enter 發佈
發佈
取消