服务提供了Join、Say、Whisper与Leave等接口方法,向对应的是回调接口的接口方法。在实现IChat服务接口的服务类ChatService中,定义了委托ChatEventHandler与ChatEventHandler类型的事件ChatEvent,正是通过它实现了识别了客户的消息广播。方法如下:
private void BroadcastMessage(ChatEventArgs e) { ChatEventHandler temp = ChatEvent; if (temp != null) { foreach (ChatEventHandler handler in temp.GetInvocationList()) { handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null); } } }
|
在客户端加入聊天室程序之前,该客户端并没有订阅ChatEvent事件,此时调用BroadcastMessage方法,在通过GetInvocationList方法获取MulticastDelegate时,不存在该客户端的委托实例。因而,其他客户在通过聊天室进行聊天时,不会将聊天信息发送到该客户端。体现在程序中,就是Join方法的如下代码片断:
myEventHandler = new ChatEventHandler(MyEventHandler);…… callback = OperationContext.Current.GetCallbackChannel(); ChatEventArgs e = new ChatEventArgs(); e.msgType = MessageType.UserEnter; e.name = name; BroadcastMessage(e); ChatEvent += myEventHandler; ……
|
注意看,ChatEvent += myEventHandler语句是放在BroadcastMessage方法调用之后。一旦该客户端加入聊天室程序之后,再调用BroadcastMessage方法,该客户端就能接收消息了。
ChatEvent事件指向的方法是MyEventHandler,该方法将执行回调接口的相关方法:
private void MyEventHandler(object sender, ChatEventArgs e) { try { switch (e.msgType) { case MessageType.Receive: callback.Receive(e.name, e.message); break; case MessageType.ReceiveWhisper: callback.ReceiveWhisper(e.name, e.message); break; case MessageType.UserEnter: callback.UserEnter(e.name); break; case MessageType.UserLeave: callback.UserLeave(e.name); break; } } catch { Leave(); } }
|
还需要注意的是Whisper方法。由于它实现了私聊功能,因而向指定客户发送信息时,不应该采用广播方式。如何找到指定客户呢?这需要一个Dictionary集合,保存客户名和与之对应的ChatEventHandler实例。在执行Whisper方法时,就可以根据客户名找到对应的ChatEventHandler实例进行调用:
public void Whisper(string to, string msg) { ChatEventArgs e = new ChatEventArgs(); e.msgType = MessageType.ReceiveWhisper; e.name = this.name; e.message = msg; try { ChatEventHandler chatterTo; lock (syncObj) { chatterTo = chatters[to]; } chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null); } catch (KeyNotFoundException) { } }
|
在客户端代码中,服务接口的调用采用了异步调用的方式,例如客户端加入聊天室:
proxy = new ChatProxy(site); IAsyncResult iar = proxy.BeginJoin(myNick, new AsyncCallback(OnEndJoin), null);
|
运行聊天室程序时,服务端仅需要提供稳定而持续的服务。聊天的参与者均为客户端用户。因而服务端的运行代码如下所示:
Uri uri = new Uri(ConfigurationManager.AppSettings["addr"]); ServiceHost host = new ServiceHost(typeof(NikeSoftChat.ChatService), uri); host.Open(); Console.WriteLine("Chat service listen on endpoint {0}", uri.ToString()); Console.WriteLine("Press ENTER to stop chat service..."); Console.ReadLine(); host.Abort(); host.Close();
|
本文Sample的作者是Nikola Paljetak。鉴于作者本人在代码所附的许可声明,为了帮助大家阅读本文,在此附上Nikola Paljetak的Sample,你可以在WCF官方网站中找到它。Nikola Paljetak的许可声明如下:
Permission is granted to anyone to use this software for any purpose, including commercial applications.
【责任编辑:碧海蓝天 TEL:(010)68476606】