今天,我們來做一個可以羣聊的應用,就像QQ羣那樣,一個服務器端,N個客戶端,服務器端運行後,每個客戶端啓動的時候會自動連接服務器生成會話,只要其中任一個客戶端向服務器發送消息,服務器都會將消息羣發到所有客戶端。
用到的知識點:
- 在進程中承載WCF服務。
- 會話的使用。
- 回調。
/// <summary>
/// 1、定義回調接口
/// </summary>
public interface ICallback
{
[OperationContract(IsOneWay=true)]
void SendToClients(string nickname, string message, DateTime time);
}
/// <summary>
/// 2、定義服務協定
/// </summary>
[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 nickname, string message, DateTime time);
[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]
void End();
/*Begin方法和End方法分別是啓動會話和終止會話,這樣每接入一個客戶端連接就會實例化一個服務類;
* 這樣就可以確保每個客戶端都與服務器維持一個會話。
*/
}
/// <summary>
/// 3、實現服務協定
/// </summary>
[ServiceBehavior(IncludeExceptionDetailInFaults=true,InstanceContextMode= InstanceContextMode.PerSession)]
public class MyService : IService
{
//聲明一個靜態的字典,存儲所有客戶端的會話ID所對應的回調
public static Dictionary<string, ICallback> m_clientCallbacks = new Dictionary<string, ICallback>();
/// <summary>
/// 開始會話,
/// 獲取所有客戶端的會話ID和回調,並將其存入字典
/// </summary>
public void Begin()
{
string sessionId = OperationContext.Current.SessionId;
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
if (sessionId != null) m_clientCallbacks[sessionId] = callback;
}
/// <summary>
/// 從字典中取出所有回調,
/// 並把接收的參數傳給回調方法;
/// 在回調調用後,這些消息就會轉發到所有連接的客戶端,從而實現羣聊。
/// </summary>
/// <param name="nickname">用戶暱稱</param>
/// <param name="message">發送的信息</param>
/// <param name="time">時間</param>
public void SendMessage(string nickname, string message, DateTime time)
{
foreach (ICallback cb in m_clientCallbacks.Values)
{
if (cb != null)
{
cb.SendToClients(nickname, message, time);
}
}
}
/// <summary>
/// 結束會話,
/// 將對應的會話ID和回調從字典中移除
/// </summary>
public void End()
{
string sessionId = OperationContext.Current.SessionId;
if (m_clientCallbacks.ContainsKey(sessionId))
{
m_clientCallbacks.Remove(sessionId);
}
}
}
這裏在類中,聲明瞭一個靜態的Dictionary<string, ICallBack>,這是一個字典,我想大家猜到了,靜態變量是基於類的,與實例無關,我們可以把它當作全局數據,在字典集合中保存所有接入客戶端的回調,由於每個會話的ID是唯一的,因此,用SessionID作爲Key是比較好操作的。
//4、配置服務器
static void Main()
{
//服務基地址
Uri baseUri = new Uri("http://localhost:3000/Service");
//聲明服務器主機
using (ServiceHost host = new ServiceHost(typeof(MyService), baseUri))
{
//添加綁定和終結點
NetTcpBinding binding = new NetTcpBinding();
host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:2000/test");
//元數據
ServiceMetadataBehavior mb = new ServiceMetadataBehavior();
mb.HttpGetEnabled = true;
mb.HttpGetUrl = new Uri("http://localhost:1234/WSDL");
host.Description.Behaviors.Add(mb);
//啓動服務
try
{
host.Open();
Console.WriteLine("服務已啓動");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
//關閉服務
host.Close();
}
}
2、實現客戶端
這裏我做了兩個客戶端,一個是winform的,一個是wpf的。我只粘貼主要代碼,其餘我會上傳到我的資源,大家可以去那裏下載
/// <summary>
/// 實現回調接口
/// </summary>
public class MyCallback : IServiceCallback
{
//事件
public event EventHandler<CallbcakEventArgs> MessageReceiveEvent;
//回調中的方法是服務器調用的,
//在客戶端要想及時偵聽到該方法被調用,可以使用事件;
//當回調方法被調用,就會觸發事件.
public void SendToClients(string nickname, string message, DateTime time)
{
if (MessageReceiveEvent != null)
{
CallbcakEventArgs args = new CallbcakEventArgs(nickname, message, time);
MessageReceiveEvent(this,args);
}
}
}
/// <summary>
/// 事件參數類
/// </summary>
public class CallbcakEventArgs:EventArgs
{
private readonly string m_nickname;
private readonly string m_message;
private readonly DateTime m_time;
public CallbcakEventArgs(string nickname,string message,DateTime time)
{
m_nickname = nickname;
m_message = message;
m_time = time;
}
public string NickName
{
get { return m_nickname; }
}
public string Message
{
get { return m_message; }
}
public DateTime Time
{
get { return m_time; }
}
}
截圖如下:
最後放上最近wcf學習的所有源碼:wcf學習筆記源碼