程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 傳說中的WCF:群聊天程序

傳說中的WCF:群聊天程序

編輯:C#入門知識

前面吹了不少重點知識了,為了可以較為綜合地運用它們,今天,我們來做一個可以群聊的應用,就像QQ群那樣,一個服務器端,N個客戶端,服務器端運行後,每個客戶端啟動的時候會自動連接服務器生成會話,只要其中任一個客戶端向服務器發送消息,服務器都會將消息群發到所有客戶端。   我們來看看如何用WCF來取代Socket。       這個例子會用到以下知識點:   在進程中承載WCF服務。 會話的使用。 回調。 在下面說明過程中,我不會粘貼所有代碼,畢竟有點長,我只放出重要部分,隨後我會將源碼上傳到【資源】。   一、服務協定和回調協定。 [csharp]   [ServiceContract(CallbackContract = typeof(ICallBack), SessionMode = SessionMode.Required)]   public interface IService   {       [OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]       void Begin();       [OperationContract(IsOneWay = true)]       void SendMessage(string nick, string msg, DateTime sendTime);       [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]       void End();   }         public interface ICallBack   {       [OperationContract(IsOneWay = true)]       void SendToClients(string nick, string msg, DateTime sendTime);   }         [ServiceContract(CallbackContract = typeof(ICallBack), SessionMode = SessionMode.Required)]     public interface IService     {         [OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]         void Begin();         [OperationContract(IsOneWay = true)]         void SendMessage(string nick, string msg, DateTime sendTime);         [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]         void End();     }         public interface ICallBack     {         [OperationContract(IsOneWay = true)]         void SendToClients(string nick, string msg, DateTime sendTime);     }Begin方法和End方法分別是啟動會話和終止會話,這樣,每接入一個客戶端連接就會實例化一個服務類(前面文章中提過了),這樣就可以確保每個客戶端都與服務器維持一個會話。   向服務器發送的消息包括用戶的昵稱、消息內容和發送時間。而回調協定中的方法也是這幾個參數,但不同的是,服務協定是客戶端調用服務器端,而回調中是服務器端調用客戶端。       二、實現服務類。 [csharp]   [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerSession)]   public class MyService : IService   {       public static Dictionary<string, ICallBack> ClientCallbacks = new Dictionary<string, ICallBack>();       public void Begin()       {           string sessionID = OperationContext.Current.SessionId;           ICallBack cb = OperationContext.Current.GetCallbackChannel<ICallBack>();           MyService.ClientCallbacks[sessionID] = cb;       }          public void SendMessage(string nick, string msg, DateTime sendTime)       {           foreach (ICallBack c in MyService.ClientCallbacks.Values.ToArray())           {               if (c != null)               {                   c.SendToClients(nick, msg, sendTime);               }           }       }          public void End()       {           string sessionID = OperationContext.Current.SessionId;           if (MyService.ClientCallbacks.ContainsKey(sessionID))           {               MyService.ClientCallbacks.Remove(sessionID);           }       }   }         [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerSession)]     public class MyService : IService     {         public static Dictionary<string, ICallBack> ClientCallbacks = new Dictionary<string, ICallBack>();         public void Begin()         {             string sessionID = OperationContext.Current.SessionId;             ICallBack cb = OperationContext.Current.GetCallbackChannel<ICallBack>();             MyService.ClientCallbacks[sessionID] = cb;         }           public void SendMessage(string nick, string msg, DateTime sendTime)         {             foreach (ICallBack c in MyService.ClientCallbacks.Values.ToArray())             {                 if (c != null)                 {                     c.SendToClients(nick, msg, sendTime);                 }             }         }           public void End()         {             string sessionID = OperationContext.Current.SessionId;             if (MyService.ClientCallbacks.ContainsKey(sessionID))             {                 MyService.ClientCallbacks.Remove(sessionID);             }         }     }我本來在代碼中寫了注釋的,不過剛剛刪了,我希望大家能在沒有注釋的前提下看懂代碼。   在類中,聲明了一個靜態的Dictionary<string, ICallBack>,這是一個字典,我想大家猜到了,靜態變量是基於類的,與實例無關,我們可以把它當作全局數據,在字典集合中保存所有接入客戶端的回調,由於每個會話的ID是唯一的,因此,用SessionID作為Key是比較好操作的。   a、在Begin方法調用時,我們把傳入的客戶端的會話ID和回調存進字典中,在End方法調用時,說明會話要終結,這時候把對應的會話ID和回調從字典中刪除。   b、在SendMessage方法調用時,從字典中取出所有回調,並把接收的參數傳給回調方法,在回調調用後,這些消息就會轉發到所有連接的客戶端。   這樣就能實現群聊了。       三、建立服務器。 [csharp]   static void Main(string[] args)   {       Console.Title = "WCF服務器端";       Uri baseAddr = new Uri("http://127.0.0.1:2713/wcfsv");       using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddr))       {           NetTcpBinding binding = new NetTcpBinding();           // 不需要任何安全驗證            binding.Security.Mode = SecurityMode.None;           // 添加終結點            host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://127.0.0.1:1736/channel");           // 元數據            ServiceMetadataBehavior mb = new ServiceMetadataBehavior();           mb.HttpGetEnabled = true;           mb.HttpGetUrl = new Uri("http://127.0.0.1:87/WSDL");           host.Description.Behaviors.Add(mb);           host.Opened += (source, arg) =>               {                   Console.WriteLine("服務已經啟動。");               };           // 打開服務            try           {               host.Open();           }           catch (Exception ex)           {               Console.WriteLine(ex.Message);           }              Console.ReadKey();       }   }             static void Main(string[] args)         {             Console.Title = "WCF服務器端";             Uri baseAddr = new Uri("http://127.0.0.1:2713/wcfsv");             using (ServiceHost host = new ServiceHost(typeof(MyService), baseAddr))             {                 NetTcpBinding binding = new NetTcpBinding();                 // 不需要任何安全驗證                 binding.Security.Mode = SecurityMode.None;                 // 添加終結點                 host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://127.0.0.1:1736/channel");                 // 元數據                 ServiceMetadataBehavior mb = new ServiceMetadataBehavior();                 mb.HttpGetEnabled = true;                 mb.HttpGetUrl = new Uri("http://127.0.0.1:87/WSDL");                 host.Description.Behaviors.Add(mb);                 host.Opened += (source, arg) =>                     {                         Console.WriteLine("服務已經啟動。");                     };                 // 打開服務                 try                 {                     host.Open();                 }                 catch (Exception ex)                 {                     Console.WriteLine(ex.Message);                 }                   Console.ReadKey();             }         }因為TCP支持會話,而且傳輸速度快,所以終結點使用net.tcp綁定。         四、在客戶端實現回調協定。 首先在客戶端引用服務,然後實現回調協定接口。   [csharp]   public class MyCallBack : WS.IServiceCallback   {       public void SendToClients(string nick, string msg, DateTime sendTime)       {           if (this.MessageReceived != null)           {               CallbackRecEventArgs arg = new CallbackRecEventArgs(nick, msg, sendTime);               this.MessageReceived(this, arg);                          }       }          // 事件        public event EventHandler<CallbackRecEventArgs> MessageReceived;   }         public class MyCallBack : WS.IServiceCallback     {         public void SendToClients(string nick, string msg, DateTime sendTime)         {             if (this.MessageReceived != null)             {                 CallbackRecEventArgs arg = new CallbackRecEventArgs(nick, msg, sendTime);                 this.MessageReceived(this, arg);                              }         }           // 事件         public event EventHandler<CallbackRecEventArgs> MessageReceived;     } 還有一個事件參數類。   [csharp]   public class CallbackRecEventArgs : EventArgs   {       string _Nick, _Msg;       DateTime _time;          public CallbackRecEventArgs(string nk, string m, DateTime t)       {           _Nick = nk;           _Msg = m;           _time = t;       }          public string Nick       {           get { return _Nick; }       }       public string MessageCont       {           get { return _Msg; }       }       public DateTime SendTime       {           get { return _time; }       }   }         public class CallbackRecEventArgs : EventArgs     {         string _Nick, _Msg;         DateTime _time;           public CallbackRecEventArgs(string nk, string m, DateTime t)         {             _Nick = nk;             _Msg = m;             _time = t;         }           public string Nick         {             get { return _Nick; }         }         public string MessageCont         {             get { return _Msg; }         }         public DateTime SendTime         {             get { return _time; }         }     }   因為回調中的方法是服務器凋用的,記住了,它不是客戶端調用的。在客戶要想及時偵聽到該方法被調用,可以使用事件,當回調方法被調用,就會觸發事件,而事件的另一個發處是,它可以在其他類中定義處理方法。   比如這樣。   [csharp]   cb = new MyCallBack();   cb.MessageReceived += cb_MessageReceived;                 cb = new MyCallBack();             cb.MessageReceived += cb_MessageReceived;     [csharp] view plaincopyprint? void cb_MessageReceived(object sender, CallbackRecEventArgs e)   {       MessageEnt msg = new MessageEnt();       msg.NickName = e.Nick;       msg.Message = e.MessageCont;       msg.Time = e.SendTime;       if (e.Nick == MyNickName)       {           msg.IsMe = true;       }       else       {           msg.IsMe = false;       }       this.dataSource.Add(msg);        }             void cb_MessageReceived(object sender, CallbackRecEventArgs e)         {             MessageEnt msg = new MessageEnt();             msg.NickName = e.Nick;             msg.Message = e.MessageCont;             msg.Time = e.SendTime;             if (e.Nick == MyNickName)             {                 msg.IsMe = true;             }             else             {                 msg.IsMe = false;             }             this.dataSource.Add(msg);                    } 其他的可以參考我們隨後上傳的源碼。   我們看看運行的結果是怎麼樣的。

\

 

\

 

\

                         

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved