分享WCF聊天程序--WCFChat
无意中在一个国外的站点下到了一个利用WCF实现聊天的程序,作者是:Nikola Paljetak。研究了一下,自己做了测试和部分修改,感觉还不错,分享给大家。
先来看下运行效果:
开启服务:
客户端程序:
程序分为客户端和服务器端:
------------服务器端:
IChatService.cs:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Runtime.Serialization;
usingSystem.ServiceModel;
usingSystem.Text;
usingSystem.Collections;
namespaceWCFChatService
{
//SessionMode.Required允许Session会话。双工协定时的回调协定类型为IChatCallback接口
[ServiceContract(SessionMode=SessionMode.Required,CallbackContract=typeof(IChatCallback))]
publicinterfaceIChatService
{
[OperationContract(IsOneWay=false,IsInitiating=true,IsTerminating=false)]//----->IsOneWay=false等待服务器完成对方法处理;IsInitiating=true启动Session会话,IsTerminating=false设置服务器发送回复后不关闭会话
string[]Join(stringname);//用户加入
[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=false)]
voidSay(stringmsg);//群聊信息
[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=false)]
voidWhisper(stringto,stringmsg);//私聊信息
[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]
voidLeave();//用户加入
}
///<summary>
///双向通信的回调接口
///</summary>
interfaceIChatCallback
{
[OperationContract(IsOneWay=true)]
voidReceive(stringsenderName,stringmessage);
[OperationContract(IsOneWay=true)]
voidReceiveWhisper(stringsenderName,stringmessage);
[OperationContract(IsOneWay=true)]
voidUserEnter(stringname);
[OperationContract(IsOneWay=true)]
voidUserLeave(stringname);
}
///<summary>
///设定消息的类型
///</summary>
publicenumMessageType{Receive,UserEnter,UserLeave,ReceiveWhisper};
///<summary>
///定义一个本例的事件消息类.创建包含有关事件的其他有用的信息的变量,只要派生自EventArgs即可。
///</summary>
publicclassChatEventArgs:EventArgs
{
publicMessageTypemsgType;
publicstringname;
publicstringmessage;
}
}
ChatService.cs
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Runtime.Serialization;
usingSystem.ServiceModel;
usingSystem.Text;
namespaceWCFChatService
{
//InstanceContextMode.PerSession服务器为每个客户会话创建一个新的上下文对象。ConcurrencyMode.Multiple异步的多线程实例
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,ConcurrencyMode=ConcurrencyMode.Multiple)]
publicclassChatService:IChatService
{
privatestaticObjectsyncObj=newObject();////定义一个静态对象用于线程部份代码块的锁定,用于lock操作
IChatCallbackcallback=null;
publicdelegatevoidChatEventHandler(objectsender,ChatEventArgse);//定义用于把处理程序赋予给事件的委托。
publicstaticeventChatEventHandlerChatEvent;//定义事件
staticDictionary<string,ChatEventHandler>chatters=newDictionary<string,ChatEventHandler>();//创建一个静态Dictionary(表示键和值)集合(字典),用于记录在线成员,Dictionary<(Of<(TKey,TValue>)>)泛型类
privatestringname;
privateChatEventHandlermyEventHandler=null;
publicstring[]Join(stringname)
{
booluserAdded=false;
myEventHandler=newChatEventHandler(MyEventHandler);//将MyEventHandler方法作为参数传递给委托
lock(syncObj)//线程的同步性,同步访问多个线程的任何变量,利用lock(独占锁),确保数据访问的唯一性。
{
if(!chatters.ContainsKey(name)&&name!=""&&name!=null)
{
this.name=name;
chatters.Add(name,MyEventHandler);
userAdded=true;
}
}
if(userAdded)
{
callback=OperationContext.Current.GetCallbackChannel<IChatCallback>();//获取当前操作客户端实例的通道给IChatCallback接口的实例callback,此通道是一个定义为IChatCallback类型的泛类型,通道的类型是事先服务契约协定好的双工机制。
ChatEventArgse=newChatEventArgs();//实例化事件消息类ChatEventArgs
e.msgType=MessageType.UserEnter;
e.name=name;
BroadcastMessage(e);
ChatEvent+=myEventHandler;
string[]list=newstring[chatters.Count];//以下代码返回当前进入聊天室成员的称列表
lock(syncObj)
{
chatters.Keys.CopyTo(list,0);//将字典中记录的用户信息复制到数组中返回。
}
returnlist;
}
else
{
returnnull;
}
}
publicvoidSay(stringmsg)
{
ChatEventArgse=newChatEventArgs();
e.msgType=MessageType.Receive;
e.name=this.name;
e.message=msg;
BroadcastMessage(e);
}
publicvoidWhisper(stringto,stringmsg)
{
ChatEventArgse=newChatEventArgs();
e.msgType=MessageType.ReceiveWhisper;
e.name=this.name;
e.message=msg;
try
{
ChatEventHandlerchatterTo;//创建一个临时委托实例
lock(syncObj)
{
chatterTo=chatters[to];//查找成员字典中,找到要接收者的委托调用
}
chatterTo.BeginInvoke(this,e,newAsyncCallback(EndAsync),null);//异步方式调用接收者的委托调用
}
catch(KeyNotFoundException)
{
}
}
publicvoidLeave()
{
if(this.name==null)
return;
lock(syncObj)
{
chatters.Remove(this.name);
}
ChatEvent-=myEventHandler;
ChatEventArgse=newChatEventArgs();
e.msgType=MessageType.UserLeave;
e.name=this.name;
this.name=null;
BroadcastMessage(e);
}
//回调,根据客户端动作通知对应客户端执行对应的操作
privatevoidMyEventHandler(objectsender,ChatEventArgse)
{
try
{
switch(e.msgType)
{
caseMessageType.Receive:
callback.Receive(e.name,e.message);
break;
caseMessageType.ReceiveWhisper:
callback.ReceiveWhisper(e.name,e.message);
break;
caseMessageType.UserEnter:
callback.UserEnter(e.name);
break;
caseMessageType.UserLeave:
callback.UserLeave(e.name);
break;
}
}
catch
{
Leave();
}
}
privatevoidBroadcastMessage(ChatEventArgse)
{
ChatEventHandlertemp=ChatEvent;
if(temp!=null)
{
//循环将在线的用户广播信息
foreach(ChatEventHandlerhandlerintemp.GetInvocationList())
{
//异步方式调用多路广播委托的调用列表中的ChatEventHandler
handler.BeginInvoke(this,e,newAsyncCallback(EndAsync),null);
}
}
}
//广播中线程调用完成的回调方法功能:清除异常多路广播委托的调用列表中异常对象(空对象)
privatevoidEndAsync(IAsyncResultar)
{
ChatEventHandlerd=null;
try
{
//封装异步委托上的异步操作结果
System.Runtime.Remoting.Messaging.AsyncResultasres=(System.Runtime.Remoting.Messaging.AsyncResult)ar;
d=((ChatEventHandler)asres.AsyncDelegate);
d.EndInvoke(ar);
}
catch
{
ChatEvent-=d;
}
}
}
}
------------客户端:
<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Runtime.InteropServices;
usingSystem.ServiceModel;
namespaceWCFChatClient
{
publicpartialclassChatForm:Form,IChatServiceCallback
{
///<summary>
///该函数将指定的消息发送到一个或多个窗口。此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回。
///</summary>
///<paramname="hWnd">其窗口程序将接收消息的窗口的句柄</param>
///<paramname="msg">指定被发送的消息</param>
///<paramname="wParam">指定附加的消息指定信息</param>
///<paramname="lParam">指定附加的消息指定信息</param>
[DllImport("user32.dll")]
privatestaticexternintSendMessage(IntPtrhWnd,intmsg,intwParam,IntPtrlParam);
//当一个窗口标准垂直滚动条产生一个滚动事件时发送此消息给那个窗口,也发送给拥有它的控件
privateconstintWM_VSCROLL=0x115;
privateconstintSB_BOTTOM=7;
privateintlastSelectedIndex=-1;
privateChatServiceClientproxy;
privatestringuserName;
privateWaitFormwfDlg=newWaitForm();
privatedelegatevoidHandleDelegate(string[]list);
privatedelegatevoidHandleErrorDelegate();
publicChatForm()
{
InitializeComponent();
ShowInterChatMenuItem(true);
}
///<summary>
///连接服务器
///</summary>
privatevoidInterChatMenuItem_Click(objectsender,EventArgse)
{
lbOnlineUsers.Items.Clear();
LoginFormloginDlg=newLoginForm();
if(loginDlg.ShowDialog()==DialogResult.OK)
{
userName=loginDlg.txtUserName.Text;
loginDlg.Close();
}
txtChatContent.Focus();
Application.DoEvents();
InstanceContextsite=newInstanceContext(this);//为实现服务实例的对象进行初始化
proxy=newChatServiceClient(site);
IAsyncResultiar=proxy.BeginJoin(userName,newAsyncCallback(OnEndJoin),null);
wfDlg.ShowDialog();
}
privatevoidOnEndJoin(IAsyncResultiar)
{
try
{
string[]list=proxy.EndJoin(iar);
HandleEndJoin(list);
}
catch(Exceptione)
{
HandleEndJoinError();
}
}
///<summary>
///错误提示
///</summary>
privatevoidHandleEndJoinError()
{
if(wfDlg.InvokeRequired)
wfDlg.Invoke(newHandleErrorDelegate(HandleEndJoinError));
else
{
wfDlg.ShowError("无法连接聊天室!");
ExitChatSession();
}
}
///<summary>
///登录结束后的处理
///</summary>
///<paramname="list"></param>
privatevoidHandleEndJoin(string[]list)
{
if(wfDlg.InvokeRequired)
wfDlg.Invoke(newHandleDelegate(HandleEndJoin),newobject[]{list});
else
{
wfDlg.Visible=false;
ShowInterChatMenuItem(false);
foreach(stringnameinlist)
{
lbOnlineUsers.Items.Add(name);
}
AppendText("用户:"+userName+"--------登录---------"+DateTime.Now.ToString()+Environment.NewLine);
}
}
///<summary>
///退出聊天室
///</summary>
privatevoidOutInterChatMenuItem_Click(objectsender,EventArgse)
{
ExitChatSession();
Application.Exit();
}
///<summary>
///群聊
///</summary>
privatevoidbtnChat_Click(objectsender,EventArgse)
{
SayAndClear("",txtChatContent.Text,false);
txtChatContent.Focus();
}
///<summary>
///发送消息
///</summary>
privatevoidSayAndClear(stringto,stringmsg,boolpvt)
{
if(msg!="")
{
try
{
CommunicationStatecs=proxy.State;
//pvt公聊还是私聊
if(!pvt)
{
proxy.Say(msg);
}
else
{
proxy.Whisper(to,msg);
}
txtChatContent.Text="";
}
catch
{
AbortProxyAndUpdateUI();
AppendText("失去连接:"+DateTime.Now.ToString()+Environment.NewLine);
ExitChatSession();
}
}
}
privatevoidtxtChatContent_KeyPress(objectsender,KeyPressEventArgse)
{
if(e.KeyChar==13)
{
e.Handled=true;
btnChat.PerformClick();
}
}
///<summary>
///只有选择一个用户时,私聊按钮才可用
///</summary>
privatevoidlbOnlineUsers_SelectedIndexChanged(objectsender,EventArgse)
{
AdjustWhisperButton();
}
///<summary>
///私聊
///</summary>
privatevoidbtnWhisper_Click(objectsender,EventArgse)
{
if(txtChatDetails.Text=="")
{
return;
}
objectto=lbOnlineUsers.SelectedItem;
if(to!=null)
{
stringreceiverName=(string)to;
AppendText("私下对"+receiverName+"说:"+txtChatContent.Text);//+Environment.NewLine
SayAndClear(receiverName,txtChatContent.Text,true);
txtChatContent.Focus();
}
}
///<summary>
///连接聊天室
///</summary>
privatevoidShowInterChatMenuItem(boolshow)
{
InterChatMenuItem.Enabled=show;
OutInterChatMenuItem.Enabled=this.btnChat.Enabled=!show;
}
privatevoidAppendText(stringtext)
{
txtChatDetails.Text+=text;
SendMessage(txtChatDetails.Handle,WM_VSCROLL,SB_BOTTOM,newIntPtr(0));
}
///<summary>
///退出应用程序时,释放使用资源
///</summary>
privatevoidExitChatSession()
{
try
{
proxy.Leave();
}
catch{}
finally
{
AbortProxyAndUpdateUI();
}
}
///<summary>
///释放使用资源
///</summary>
privatevoidAbortProxyAndUpdateUI()
{
if(proxy!=null)
{
proxy.Abort();
proxy.Close();
proxy=null;
}
ShowInterChatMenuItem(true);
}
///<summary>
///接收消息
///</summary>
publicvoidReceive(stringsenderName,stringmessage)
{
AppendText(senderName+"说:"+message+Environment.NewLine);
}
///<summary>
///接收私聊消息
///</summary>
publicvoidReceiveWhisper(stringsenderName,stringmessage)
{
AppendText(senderName+"私下说:"+message+Environment.NewLine);
}
///<summary>
///新用户登录
///</summary>
publicvoidUserEnter(stringname)
{
AppendText("用户"+name+"--------登录---------"+DateTime.Now.ToString()+Environment.NewLine);
lbOnlineUsers.Items.Add(name);
}
///<summary>
///用户离开
///</summary>
publicvoidUserLeave(stringname)
{
AppendText("用户"+name+"--------离开---------"+DateTime.Now.ToString()+Environment.NewLine);
lbOnlineUsers.Items.Remove(name);
AdjustWhisperButton();
}
///<summary>
///控制私聊按钮的可用性,只有选择了用户时按钮才可用
///</summary>
privatevoidAdjustWhisperButton()
{
if(lbOnlineUsers.SelectedIndex==lastSelectedIndex)
{
lbOnlineUsers.SelectedIndex=-1;
lastSelectedIndex=-1;
btnWhisper.Enabled=false;
}
else
{
btnWhisper.Enabled=true;
lastSelectedIndex=lbOnlineUsers.SelectedIndex;
}
txtChatContent.Focus();
}
///<summary>