今天,我们来做一个可以群聊的应用,就像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学习笔记源码