利用WCF实现一个局域网的聊天室,是一件很简单的事情。不过还是需要稍微了解一下WCF的基本知识,并最好能够写几个小程序试试。推荐Artech的WCF系列。
首先说一个我的实现思路,设A为服务,B1,B2,B3为三个客户端。B1发送消息到A,然后A把消息转发给B1,B2,B3,这样聊天室就基本差不多了。用到的WCF绑定肯定是支持回调的,我用的是WsDualHttpBinding。
下面是Service的契约代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.ServiceModel; 6 7 namespace CTalk2_0_Service 8 { 9 [ServiceContract(CallbackContract = typeof (IServiceContractCallback))] 10 interface IServiceContract 11 { 12 [OperationContract(IsOneWay = true )] 13 void Connect(); 14 [OperationContract(IsOneWay = true )] 15 void DisConnect(); 16 [OperationContract(IsOneWay = true )] 17 void ShowOnLineUsers(UserInfo userinfo, bool flag); 18 [OperationContract(IsOneWay = true )] 19 void ShowMessage(Message message); 20 } 21 22 interface IServiceContractCallback 23 { 24 [OperationContract(IsOneWay = true )] 25 void UpdateNameList(List < UserInfo > list, bool flag); 26 [OperationContract(IsOneWay = true )] 27 void UpdateMessageList(Message message); 28 } 29 30 [Serializable] 31 public class UserInfo 32 { 33 public string Name; 34 public string Sex; 35 } 36 [Serializable] 37 public class Message 38 { 39 public string Name; 40 public string Words; 41 } 42 } 服务端有Connect和Disconnect两种方法,一个客户端连入聊天室的时候,调用Connect,把回调对象传给服务。服务端将此回调对象存下,以便将来进行操作。当客户端要退出聊天室的时候,调用DisConnect方法,这样服务端就会移去此客户端对应的回调对象,这样就可以保证回调对象的有效性。
至于为什么方法都是OneWay = true呢?可以看下这篇文章:
我的理解,如果不设置OneWay,那么就是Request/Response的模式,意味着,一旦Client发出请求,就阻塞自己,获得了Server的回应后,才继续运行。引入了回调,就变得更加复杂。主要就是线程死锁的问题。
下面是Service的实现代码:
View Code 1 [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.PerCall)] 2 public class MyService : IServiceContract 3 { 4 static List < IServiceContractCallback > list = new List < IServiceContractCallback > (); 5 static List < UserInfo > userList = new List < UserInfo > (); 6 7 public void Connect() 8 { 9 IServiceContractCallback callback = OperationContext.Current.GetCallbackChannel < IServiceContractCallback > (); 10 if (list.Contains(callback) == false ) 11 { 12 list.Add(callback); 13 } 14 Console.WriteLine( " 当前在线用户数: " + list.Count()); 15 } 16 17 public void DisConnect() 18 { 19 IServiceContractCallback callback = OperationContext.Current.GetCallbackChannel < IServiceContractCallback > (); 20 if (list.Contains(callback) == true ) 21 { 22 list.Remove(callback); 23 } 24 Console.WriteLine( " 当前在线用户数: " + list.Count()); 25 } 26 27 public void ShowOnLineUsers(UserInfo userinfo, bool flag) 28 { 29 if (flag == true ) 30 { 31 Func < UserInfo, bool > function1 = delegate (UserInfo user) 32 { 33 if (user.Name == userinfo.Name && user.Sex == userinfo.Sex) return false ; 34 else return true ; 35 }; 36 if (userList.All < UserInfo > (function1) == true ) userList.Add(userinfo); 37 } 38 else 39 { 40 Func < UserInfo, bool > function2 = delegate (UserInfo user) 41 { 42 if (user.Name == userinfo.Name && user.Sex == userinfo.Sex) return true ; 43 else return false ; 44 }; 45 if (userList.Any < UserInfo > (function2) == true ) 46 { 47 foreach (UserInfo element in userList) 48 { 49 if (element.Name == userinfo.Name && element.Sex == userinfo.Sex) element.Name = " RemoveFlag " ; 50 } 51 } 52 } 53 if (userList.Count == 0 ) userList.Add(userinfo); 54 List < UserInfo > userList1 = new List < UserInfo > (); 55 foreach (UserInfo element in userList) 56 { 57 if (element.Name != " RemoveFlag " ) userList1.Add(element); 58 } 59 Action < IServiceContractCallback > method = delegate (IServiceContractCallback callback) 60 { 61 if (callback != null ) 62 { 63 callback.UpdateNameList(userList1, flag); 64 } 65 }; 66 list.ForEach(method); 67 } 68 69 public void ShowMessage(Message message) 70 { 71 Action < IServiceContractCallback > method = delegate (IServiceContractCallback callback) 72 { 73 if (callback != null ) 74 { 75 callback.UpdateMessageList(message); 76 } 77 }; 78 list.ForEach(method); 79 } 80 } 客户端如果是Winform写的话,还有个问题需要注意,就是怎样去用客户端的服务线程去更新Winform控件的内容。Winform的控件是不能被其他线程随意更改的,因此要刷新Winform控件的内容,可以使用下面的实现方式:
1 public void BindListBox(UserInfo[] list, bool flag) 2 { 3 UpdateControlDelegate bindListBox = delegate (UserInfo[] list1, bool flag1) 4 { 5 List < string > listString = new List < string > (); 6 foreach (UserInfo element in list1) 7 { 8 listString.Add(element.Name + " " + element.Sex); 9 } 10 this .UserList.DataSource = listString; 11 this .UserList.Show(); 12 }; 13 this .Invoke(bindListBox, list, flag); 14 } 15 16 public void UpdateChatLog( string text) 17 { 18 UpdateChatLogDelegate updataChatLog = delegate ( string text1) 19 { 20 this .ChatLog.Text += text1; 21 }; 22 this .Invoke(updataChatLog, text); 23 } 24 private delegate void UpdateControlDelegate(UserInfo[] list, bool flag); 25 private delegate void UpdateChatLogDelegate( string text); 下面是截图: