重温WCF之会话Session(九)
轉載地址:http://blog.csdn.net/tcjiaan/article/details/8281782
每個客戶端在服務器上都有其的獨立數據存儲區,互不相干,就好像A和服務器在單獨談話一樣,所以叫會話。
?下面,我們寫一個例子,看看在不支持會話的綁定上連續調用兩個有關聯的代碼,會發生什么情況。
?
[ServiceContract]public interface IService{[OperationContract(IsOneWay = true)]void SetValue(int n);[OperationContract]int GetValue();}[ServiceBehavior(IncludeExceptionDetailInFaults = true)]public class MyService : IService{public MyService(){Console.WriteLine("-------------------------------");Console.WriteLine("{0} - 服務被實例化。",DateTime.Now.ToLongTimeString());}// 在析構函數中也輸出信息~MyService(){Console.WriteLine("{0} - 服務實例被釋放。", DateTime.Now.ToLongTimeString());Console.WriteLine("--------------------------------");}/// <summary>/// 私有字段/// </summary>private int m_Value = int.MinValue;public void SetValue(int n){this.m_Value = n;Console.WriteLine("會話ID:{0}", OperationContext.Current.SessionId);}public int GetValue(){Console.WriteLine("會話ID:{0}", OperationContext.Current.SessionId);return this.m_Value;}}在服務類中,我們分別在構造函數和析構函數中輸出一些內容,以便于在運行時看到服務什么時候被實例化,什么時候被釋放。同時,在調用每個操作協定時,我們也輸出當前會話的ID,如果空就說明當前沒有啟用會話。
接著,實現服務主機,我們使用不支持的Bindding。
static void Main(string[] args){Console.Title = "WCF服務器端";using (ServiceHost host = new ServiceHost(typeof(MyService), new Uri("http://127.0.0.1:1211/sv"))){// 綁定BasicHttpBinding binding = new BasicHttpBinding();binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式host.AddServiceEndpoint(typeof(IService), binding, "/ep");// 服務元數據ServiceMetadataBehavior mb = new ServiceMetadataBehavior();mb.HttpGetEnabled = true;mb.HttpGetUrl = new Uri("http://127.0.0.1:8008/meta");host.Description.Behaviors.Add(mb);host.Opened += (sender, arg) =>{Console.WriteLine("服務已啟動。");};try{host.Open();//打開服務 Console.Read();}catch (Exception ex){Console.WriteLine(ex.Message);}}}然后,實現客戶端,為了使演示操作更方便,客戶端使用Windows Forms項目,界面大致如下圖所示,源代碼我后面會上傳到資源。
?
服務有兩個操作,從前面服務類的定義我們知道,我在服務類內部定義了一個私有字段,而公開的兩個操作協定,分別是設置這個值和獲取這個值。
public partial class Form1 : Form{WS.ServiceClient client = null;public Form1(){InitializeComponent();client = new WS.ServiceClient();// 關閉通道this.FormClosing += (frmsender, frmargs) => client.Close();}private void btnCall1_Click(object sender, EventArgs e){int v;if (!int.TryParse(this.txtInput.Text, out v)){return;}client.SetValue(v);}private void btnCall2_Click(object sender, EventArgs e){int v = client.GetValue();this.txtOutput.Text = v.ToString();}}?
我們現在要測試一下,看看先后調用這兩個方法,能不能得到我們預期的值。即如果我調用SetValue(100),那么,在調用GetValue時應該返回100,事實是不是這樣呢?
八格牙路,我沒有看到預期的值。
?我輸入了100,可是取出來的是Min int,再看一看服務器端輸出了哪些信息。
?很明顯,每調用一次操作,服務類就被實例化一次,意思就是:調用SetValue時是實例A,而調用GetValue時可能是實例B了,所以,私有字段的值沒有被保存。
那么,如何證明兩次調用的操作不屬于同一個服務實例呢?還記得GetHashCode嗎?對的,只要在內存中不是同一個實例的,其哈希值肯定不同。是不是這樣呢,我們把上面的服務代碼改一下。
public void SetValue(int n){this.m_Value = n;Console.WriteLine("------------------------");Console.WriteLine("當前實例的哈希值:{0}", this.GetHashCode().ToString("x"));Console.WriteLine("會話ID:{0}", OperationContext.Current.SessionId);}public int GetValue(){Console.WriteLine("------------------------");Console.WriteLine("當前實例的哈希值:{0}", this.GetHashCode().ToString("x"));Console.WriteLine("會話ID:{0}", OperationContext.Current.SessionId);return this.m_Value;}?
那么,這次又會發生什么事呢,看結果。
這個結果證實了我之前的推斷,先后調用的兩個方法不是同一個實例的。
?
?
?那么,如果啟用了會話,結果又會如何呢?
[ServiceContract(SessionMode = SessionMode.Required)]public interface IService{。。。。。}在定義服務協定的時候,設置SessionMode可以讓服務要求客戶端啟用會話。
接下來,要使用支持會話的綁定。
// 綁定//BasicHttpBinding binding = new BasicHttpBinding();//binding.Security.Mode = BasicHttpSecurityMode.None;//不需要安全模式//host.AddServiceEndpoint(typeof(IService), binding, "/ep");NetTcpBinding binding = new NetTcpBinding();binding.Security.Mode = SecurityMode.None;host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://127.0.0.1:2377/ep");把客戶端的服務引用更新一下。然后看看這回會不會達到我們的目的。
?
怎么樣,高興吧?終于看到我們要的效果了,我輸入了100,取出來的還是100,這一回從服務器端的輸出看,服務類只被實例化了一次,而且看看兩個哈希值是相同的,這證明了確實是同一個實例,同時,我們也看到了兩次調用的會話ID是一樣的,這也說明了,客戶端兩次調用都基于同一個會話進行的,這么一來,輸進去的100就能順利取出來了。
你不妨多開幾個客戶端來試試。
?
?
?看到那個不同的會話ID,哈希值和實例化時間了吧?這表明了:服務器獨立維護著與每個客戶端的會話。
?
下面還要對我們的解決方案進行修改。
服務器端,把服務協定改為:
[ServiceContract(SessionMode = SessionMode.Required)]public interface IService{[OperationContract(IsOneWay = true,IsInitiating = true, IsTerminating = false)]void SetValue(int n);[OperationContract]int GetValue();[OperationContract(IsInitiating = false, IsTerminating = true)]void EndSession();}在服務類中增加對EndSession方法的實現。
public void EndSession(){Console.WriteLine("會話結束。");}?
看到變化了嗎?
我們在使用OperationContractAttribute定義操作協定時,設置了兩個屬性:
a、IsInitiating:如果為真,則當調用該操作時就啟用會話。
b、IsTerminating:如果為真,則說明當該用該操作時終結會話。
所以,上面的例子是,當調用SetValue時開始會話,當調用EndSession方法后會話結束,在選擇作為結束會話的方法時,最好使用返回值為void或者單向通訊(One Way)的方法,這樣,不用等待客戶結束才結束會話,因為單向通訊,不需要向客戶端回復消息,因為它被調用后就可以馬上終止會話了。
?
在客戶端代碼中,我們要取消之前的關閉通道的代碼,因為不再需要,會話一旦終結,通道就自動關閉,服務實例就會自動人間消失。
WS.ServiceClient client = null;public Form1(){InitializeComponent();client = new WS.ServiceClient();// 關閉通道//this.FormClosing += (frmsender, frmargs) => client.Close();}?
然后,在調用完GetValue后馬上就EndSession,看看這回又會發生什么。
這樣,一旦三個方法調用完之后,會話結束,服務實例也解放了。
再說明一下,IsInitiating = true的操作開始新會話,IsTerminating = true的操作終結會話。
總結
以上是生活随笔為你收集整理的重温WCF之会话Session(九)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在RHEV平台中新建ISO存储域
- 下一篇: 200多个js技巧代码(4)