WCF综合运用之:聊天系统
![](https://images.cnblogs.com/cnblogs_com/wanqiming/2009-09-30_092749.jpg)
整个聊天系统需要考虑下面的几点:
1、使用什么样的消息交换模式(如果不清楚参考:http://www.cnblogs.com/artech/archive/2007/03/02/661969.html):消息交换包括one-way,request/reply,duplex,前面两种相对的比较的简单,duplex关键是实现回调-服务端可以回调客户端的代码,所以我们需要定义两个接口:
public interface IChat //服务端的接口
{
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/Say")]
void Say(string msg); //公聊
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/Whisper")]
void Whisper(string to, string msg); //私聊
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false, Action = "http://tempuri.org/IChat/Connect")]
bool Connect(Person name); //连接服务端 注册当前用户
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/PersonList")]
List<Person> PersonList(Person name); //获取所有的页面信息
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true, Action = "http://tempuri.org/IChat/Leave")]
void Leave(); //注销用户
}
public interface IChatCallback //回调接口
{
[OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/Receive")]
void Receive(Person sender, string message); //接收公聊信息
[OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/ReceiveWhisper")]
void ReceiveWhisper(Person sender, string message); //接收私聊信息
[OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/UserEnter")]
void UserEnter(Person person); //接收进入聊天室的用户
[OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/UserLeave")]
void UserLeave(Person person); //移除在聊天室的用户
}
2、使用什么样的实例模型,由于我们需要需要保持客户端的状态。我们需要使用PerSession模型(如果不清楚请参见:http://www.cnblogs.com/wanqiming/archive/2009/09/17/1568401.html)。代码如下:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ChatService : IChat
{
#region Instance fields
//定义一个静态对象用于线程部份代码块的锁定,用于lock操作
private static Object syncObj = new Object();
//创建一个IChatCallback 回调接口实例,接口成员始终是公共的,所有没有访问修饰符
IChatCallback callback = null;
//定义一个委托
public delegate void ChatEventHandler(object sender, ChatEventArgs e);
//定义一个静态的委托事件
public static event ChatEventHandler ChatEvent;
//创建委托(ChatEventHandler)的一个空实例
private ChatEventHandler myEventHandler = null;
//创建一个静态Dictionary(表示键和值)集合(字典),用于记录在线成员,Dictionary<(Of <(TKey, TValue>)>) 泛型类
static Dictionary<Person, ChatEventHandler> chatters = new Dictionary<Person, ChatEventHandler>();
//当前用户
private Person person;
//确认用户是否存在
private bool checkIfPersonExists(string name)
{
foreach (Person p in chatters.Keys)
{
if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
//根据用户搜索字典中是否存在 ChatEventHandler , 以便调用执行
private ChatEventHandler getPersonHandler(string name)
{
foreach (Person p in chatters.Keys)
{
if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
ChatEventHandler chatTo = null;
chatters.TryGetValue(p, out chatTo);
return chatTo;
}
}
return null;
}
//根据用户名,寻找用户
private Person getPerson(string name)
{
foreach (Person p in chatters.Keys)
{
if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return p;
}
}
return null;
}
#endregion
#region IChat implementation
//连接服务器,如果存在重复的用户名,返回失败,否则连接成功,把当前用户加到服务器用户列表中
public bool Connect(Person person)
{
bool userAdded = false;
//创建一个新的委托代理,指向 MyEventHandler 方法
myEventHandler = new ChatEventHandler(MyEventHandler);
//锁定,保持lock块中的代码段始终只有一个线程在调用,原因是ConcurrencyMode.Multiple 为异步的多线程实例,存在并发竞争问题
lock (syncObj)
{
if (!checkIfPersonExists(person.Name) && person != null) //根据用户名判断用户名是否被占用
{
this.person = person;
chatters.Add(person, MyEventHandler); //添加当前用户到服务器列表中
userAdded = true;
}
}
if (userAdded)
{
callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.UserEnter;
e.person = this.person;
BroadcastMessage(e);
ChatEvent += myEventHandler; //把当前实例加到事件中,便于调用对客户端的回调
return false;
}
else
{
return true;
}
}
//获得聊天室中的所有用户信息
public List<Person> PersonList(Person person)
{
List<Person> list = new List<Person>();
foreach (var p in chatters.Keys)
{
if( p.Name != person.Name )
{
list.Add(p);
}
}
return list;
}
//发送信息给所有的人
public void Say(string msg)
{
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.Receive;
e.person = this.person;
e.message = msg;
BroadcastMessage(e);
}
//私聊 to 接受者姓名,msg 接受的信息
public void Whisper(string to, string msg)
{
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.ReceiveWhisper;
e.person = this.person;
e.message = msg;
try
{
ChatEventHandler chatterTo;
lock (syncObj)
{
chatterTo = getPersonHandler(to);
if (chatterTo == null)
{
throw new KeyNotFoundException("The person whos name is " + to +
" could not be found");
}
}
//异步回调
chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
}
catch (KeyNotFoundException)
{
}
}
//用户离开
public void Leave()
{
if (this.person == null)
return;
ChatEventHandler chatterToRemove = getPersonHandler(this.person.Name);
lock (syncObj)
{
chatters.Remove(this.person);
}
ChatEvent -= chatterToRemove;
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.UserLeave;
e.person = this.person;
this.person = null;
//告示所有的人
BroadcastMessage(e);
}
#endregion
#region private methods
//判断当前用户的操作
private void MyEventHandler(object sender, ChatEventArgs e)
{
try
{
switch (e.msgType)
{
case MessageType.Receive:
callback.Receive(e.person, e.message);
break;
case MessageType.ReceiveWhisper:
callback.ReceiveWhisper(e.person, e.message);
break;
case MessageType.UserEnter:
callback.UserEnter(e.person);
break;
case MessageType.UserLeave:
callback.UserLeave(e.person);
break;
}
}
catch
{
Leave();
}
}
//把信息发给所有的在线用户
private void BroadcastMessage(ChatEventArgs e)
{
ChatEventHandler temp = ChatEvent;
if (temp != null)
{
ChatEvent(this, e);
}
}
//广播中线程调用完成的回调方法
//功能:清除异常多路广播委托的调用列表中异常对象(空对象)
private void EndAsync(IAsyncResult ar)
{
ChatEventHandler d = null;
try
{
System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
d = ((ChatEventHandler)asres.AsyncDelegate);
d.EndInvoke(ar);
}
catch
{
ChatEvent -= d;
}
}
#endregion
}
![](https://images.cnblogs.com/cnblogs_com/wanqiming/2009-09-30_092427.jpg)
看代码吧,下载地址:/Files/wanqiming/Chat.rar