引言
從本質上說,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:http://www.cnblogs.com/terrylee/// </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:http://www.cnblogs.com/terrylee/// </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能夠有更深的認識。
總結
本文我們通過一個簡單的樣本介紹了在通道模型中服務端是如何接收訊息以及用戶端是如何發送訊息的,希望對大家有所協助。