分享WCF聊天程序--WCFChat實(shí)現(xiàn)代碼
無意中在一個(gè)國(guó)外的站點(diǎn)下到了一個(gè)利用WCF實(shí)現(xiàn)聊天的程序,作者是:Nikola Paljetak。研究了一下,自己做了測(cè)試和部分修改,感覺還不錯(cuò),分享給大家。
先來看下運(yùn)行效果:
開啟服務(wù):
客戶端程序:
程序分為客戶端和服務(wù)器端:
------------服務(wù)器端:
IChatService.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Collections;
namespace WCFChatService
{
// SessionMode.Required 允許Session會(huì)話。雙工協(xié)定時(shí)的回調(diào)協(xié)定類型為IChatCallback接口
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
public interface IChatService
{
[OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]//----->IsOneWay = false等待服務(wù)器完成對(duì)方法處理;IsInitiating = true啟動(dòng)Session會(huì)話,IsTerminating = false 設(shè)置服務(wù)器發(fā)送回復(fù)后不關(guān)閉會(huì)話
string[] Join(string name);//用戶加入
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void Say(string msg);//群聊信息
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
void Whisper(string to, string msg);//私聊信息
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
void Leave();//用戶加入
}
/// <summary>
/// 雙向通信的回調(diào)接口
/// </summary>
interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void Receive(string senderName, string message);
[OperationContract(IsOneWay = true)]
void ReceiveWhisper(string senderName, string message);
[OperationContract(IsOneWay = true)]
void UserEnter(string name);
[OperationContract(IsOneWay = true)]
void UserLeave(string name);
}
/// <summary>
/// 設(shè)定消息的類型
/// </summary>
public enum MessageType { Receive, UserEnter, UserLeave, ReceiveWhisper };
/// <summary>
/// 定義一個(gè)本例的事件消息類. 創(chuàng)建包含有關(guān)事件的其他有用的信息的變量,只要派生自EventArgs即可。
/// </summary>
public class ChatEventArgs : EventArgs
{
public MessageType msgType;
public string name;
public string message;
}
}
ChatService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCFChatService
{
// InstanceContextMode.PerSession 服務(wù)器為每個(gè)客戶會(huì)話創(chuàng)建一個(gè)新的上下文對(duì)象。ConcurrencyMode.Multiple 異步的多線程實(shí)例
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ChatService : IChatService
{
private static Object syncObj = new Object();////定義一個(gè)靜態(tài)對(duì)象用于線程部份代碼塊的鎖定,用于lock操作
IChatCallback callback = null;
public delegate void ChatEventHandler(object sender, ChatEventArgs e);//定義用于把處理程序賦予給事件的委托。
public static event ChatEventHandler ChatEvent;//定義事件
static Dictionary<string, ChatEventHandler> chatters = new Dictionary<string, ChatEventHandler>();//創(chuàng)建一個(gè)靜態(tài)Dictionary(表示鍵和值)集合(字典),用于記錄在線成員,Dictionary<(Of <(TKey, TValue>)>) 泛型類
private string name;
private ChatEventHandler myEventHandler = null;
public string[] Join(string name)
{
bool userAdded = false;
myEventHandler = new ChatEventHandler(MyEventHandler);//將MyEventHandler方法作為參數(shù)傳遞給委托
lock (syncObj)//線程的同步性,同步訪問多個(gè)線程的任何變量,利用lock(獨(dú)占鎖),確保數(shù)據(jù)訪問的唯一性。
{
if (!chatters.ContainsKey(name) && name != "" && name != null)
{
this.name = name;
chatters.Add(name, MyEventHandler);
userAdded = true;
}
}
if (userAdded)
{
callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();//獲取當(dāng)前操作客戶端實(shí)例的通道給IChatCallback接口的實(shí)例callback,此通道是一個(gè)定義為IChatCallback類型的泛類型,通道的類型是事先服務(wù)契約協(xié)定好的雙工機(jī)制。
ChatEventArgs e = new ChatEventArgs();//實(shí)例化事件消息類ChatEventArgs
e.msgType = MessageType.UserEnter;
e.name = name;
BroadcastMessage(e);
ChatEvent += myEventHandler;
string[] list = new string[chatters.Count]; //以下代碼返回當(dāng)前進(jìn)入聊天室成員的稱列表
lock (syncObj)
{
chatters.Keys.CopyTo(list, 0);//將字典中記錄的用戶信息復(fù)制到數(shù)組中返回。
}
return list;
}
else
{
return null;
}
}
public void Say(string msg)
{
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.Receive;
e.name = this.name;
e.message = msg;
BroadcastMessage(e);
}
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;//創(chuàng)建一個(gè)臨時(shí)委托實(shí)例
lock (syncObj)
{
chatterTo = chatters[to]; //查找成員字典中,找到要接收者的委托調(diào)用
}
chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);//異步方式調(diào)用接收者的委托調(diào)用
}
catch (KeyNotFoundException)
{
}
}
public void Leave()
{
if (this.name == null)
return;
lock (syncObj)
{
chatters.Remove(this.name);
}
ChatEvent -= myEventHandler;
ChatEventArgs e = new ChatEventArgs();
e.msgType = MessageType.UserLeave;
e.name = this.name;
this.name = null;
BroadcastMessage(e);
}
//回調(diào),根據(jù)客戶端動(dòng)作通知對(duì)應(yīng)客戶端執(zhí)行對(duì)應(yīng)的操作
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();
}
}
private void BroadcastMessage(ChatEventArgs e)
{
ChatEventHandler temp = ChatEvent;
if (temp != null)
{
//循環(huán)將在線的用戶廣播信息
foreach (ChatEventHandler handler in temp.GetInvocationList())
{
//異步方式調(diào)用多路廣播委托的調(diào)用列表中的ChatEventHandler
handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
}
}
}
//廣播中線程調(diào)用完成的回調(diào)方法功能:清除異常多路廣播委托的調(diào)用列表中異常對(duì)象(空對(duì)象)
private void EndAsync(IAsyncResult ar)
{
ChatEventHandler d = null;
try
{
//封裝異步委托上的異步操作結(jié)果
System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
d = ((ChatEventHandler)asres.AsyncDelegate);
d.EndInvoke(ar);
}
catch
{
ChatEvent -= d;
}
}
}
}
------------客戶端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.ServiceModel;
namespace WCFChatClient
{
public partial class ChatForm : Form, IChatServiceCallback
{
/// <summary>
/// 該函數(shù)將指定的消息發(fā)送到一個(gè)或多個(gè)窗口。此函數(shù)為指定的窗口調(diào)用窗口程序,直到窗口程序處理完消息再返回。
/// </summary>
/// <param name="hWnd">其窗口程序?qū)⒔邮障⒌拇翱诘木浔?lt;/param>
/// <param name="msg">指定被發(fā)送的消息</param>
/// <param name="wParam">指定附加的消息指定信息</param>
/// <param name="lParam">指定附加的消息指定信息</param>
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
//當(dāng)一個(gè)窗口標(biāo)準(zhǔn)垂直滾動(dòng)條產(chǎn)生一個(gè)滾動(dòng)事件時(shí)發(fā)送此消息給那個(gè)窗口,也發(fā)送給擁有它的控件
private const int WM_VSCROLL = 0x115;
private const int SB_BOTTOM = 7;
private int lastSelectedIndex = -1;
private ChatServiceClient proxy;
private string userName;
private WaitForm wfDlg = new WaitForm();
private delegate void HandleDelegate(string[] list);
private delegate void HandleErrorDelegate();
public ChatForm()
{
InitializeComponent();
ShowInterChatMenuItem(true);
}
/// <summary>
/// 連接服務(wù)器
/// </summary>
private void InterChatMenuItem_Click(object sender, EventArgs e)
{
lbOnlineUsers.Items.Clear();
LoginForm loginDlg = new LoginForm();
if (loginDlg.ShowDialog() == DialogResult.OK)
{
userName = loginDlg.txtUserName.Text;
loginDlg.Close();
}
txtChatContent.Focus();
Application.DoEvents();
InstanceContext site = new InstanceContext(this);//為實(shí)現(xiàn)服務(wù)實(shí)例的對(duì)象進(jìn)行初始化
proxy = new ChatServiceClient(site);
IAsyncResult iar = proxy.BeginJoin(userName, new AsyncCallback(OnEndJoin), null);
wfDlg.ShowDialog();
}
private void OnEndJoin(IAsyncResult iar)
{
try
{
string[] list = proxy.EndJoin(iar);
HandleEndJoin(list);
}
catch (Exception e)
{
HandleEndJoinError();
}
}
/// <summary>
/// 錯(cuò)誤提示
/// </summary>
private void HandleEndJoinError()
{
if (wfDlg.InvokeRequired)
wfDlg.Invoke(new HandleErrorDelegate(HandleEndJoinError));
else
{
wfDlg.ShowError("無法連接聊天室!");
ExitChatSession();
}
}
/// <summary>
/// 登錄結(jié)束后的處理
/// </summary>
/// <param name="list"></param>
private void HandleEndJoin(string[] list)
{
if (wfDlg.InvokeRequired)
wfDlg.Invoke(new HandleDelegate(HandleEndJoin), new object[] { list });
else
{
wfDlg.Visible = false;
ShowInterChatMenuItem(false);
foreach (string name in list)
{
lbOnlineUsers.Items.Add(name);
}
AppendText(" 用戶: " + userName + "--------登錄---------" + DateTime.Now.ToString()+ Environment.NewLine);
}
}
/// <summary>
/// 退出聊天室
/// </summary>
private void OutInterChatMenuItem_Click(object sender, EventArgs e)
{
ExitChatSession();
Application.Exit();
}
/// <summary>
/// 群聊
/// </summary>
private void btnChat_Click(object sender, EventArgs e)
{
SayAndClear("", txtChatContent.Text, false);
txtChatContent.Focus();
}
/// <summary>
/// 發(fā)送消息
/// </summary>
private void SayAndClear(string to, string msg, bool pvt)
{
if (msg != "")
{
try
{
CommunicationState cs = proxy.State;
//pvt 公聊還是私聊
if (!pvt)
{
proxy.Say(msg);
}
else
{
proxy.Whisper(to, msg);
}
txtChatContent.Text = "";
}
catch
{
AbortProxyAndUpdateUI();
AppendText("失去連接: " + DateTime.Now.ToString() + Environment.NewLine);
ExitChatSession();
}
}
}
private void txtChatContent_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13)
{
e.Handled = true;
btnChat.PerformClick();
}
}
/// <summary>
/// 只有選擇一個(gè)用戶時(shí),私聊按鈕才可用
/// </summary>
private void lbOnlineUsers_SelectedIndexChanged(object sender, EventArgs e)
{
AdjustWhisperButton();
}
/// <summary>
/// 私聊
/// </summary>
private void btnWhisper_Click(object sender, EventArgs e)
{
if (txtChatDetails.Text == "")
{
return;
}
object to = lbOnlineUsers.SelectedItem;
if (to != null)
{
string receiverName = (string)to;
AppendText("私下對(duì)" + receiverName + "說: " + txtChatContent.Text);//+ Environment.NewLine
SayAndClear(receiverName, txtChatContent.Text, true);
txtChatContent.Focus();
}
}
/// <summary>
/// 連接聊天室
/// </summary>
private void ShowInterChatMenuItem(bool show)
{
InterChatMenuItem.Enabled = show;
OutInterChatMenuItem.Enabled = this.btnChat.Enabled = !show;
}
private void AppendText(string text)
{
txtChatDetails.Text += text;
SendMessage(txtChatDetails.Handle, WM_VSCROLL, SB_BOTTOM, new IntPtr(0));
}
/// <summary>
/// 退出應(yīng)用程序時(shí),釋放使用資源
/// </summary>
private void ExitChatSession()
{
try
{
proxy.Leave();
}
catch { }
finally
{
AbortProxyAndUpdateUI();
}
}
/// <summary>
/// 釋放使用資源
/// </summary>
private void AbortProxyAndUpdateUI()
{
if (proxy != null)
{
proxy.Abort();
proxy.Close();
proxy = null;
}
ShowInterChatMenuItem(true);
}
/// <summary>
/// 接收消息
/// </summary>
public void Receive(string senderName, string message)
{
AppendText(senderName + "說: " + message + Environment.NewLine);
}
/// <summary>
/// 接收私聊消息
/// </summary>
public void ReceiveWhisper(string senderName, string message)
{
AppendText(senderName + " 私下說: " + message + Environment.NewLine);
}
/// <summary>
/// 新用戶登錄
/// </summary>
public void UserEnter(string name)
{
AppendText("用戶 " + name + " --------登錄---------" + DateTime.Now.ToString() + Environment.NewLine);
lbOnlineUsers.Items.Add(name);
}
/// <summary>
/// 用戶離開
/// </summary>
public void UserLeave(string name)
{
AppendText("用戶 " + name + " --------離開---------" + DateTime.Now.ToString() + Environment.NewLine);
lbOnlineUsers.Items.Remove(name);
AdjustWhisperButton();
}
/// <summary>
/// 控制私聊按鈕的可用性,只有選擇了用戶時(shí)按鈕才可用
/// </summary>
private void AdjustWhisperButton()
{
if (lbOnlineUsers.SelectedIndex == lastSelectedIndex)
{
lbOnlineUsers.SelectedIndex = -1;
lastSelectedIndex = -1;
btnWhisper.Enabled = false;
}
else
{
btnWhisper.Enabled = true;
lastSelectedIndex = lbOnlineUsers.SelectedIndex;
}
txtChatContent.Focus();
}
/// <summary>
/// 窗體關(guān)閉時(shí),釋放使用資源
/// </summary>
private void ChatForm_FormClosed(object sender, FormClosedEventArgs e)
{
AbortProxyAndUpdateUI();
Application.Exit();
}
}
}
代碼中我做了詳細(xì)的講解,相信園友們完全可以看懂。代碼中的一些使用的方法還是值得大家參考學(xué)習(xí)的。這里涉及到了WCF的使用方法,需要注意的是:如果想利用工具生成代理類,需要加上下面的代碼:
if (host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
{
BindingElement metaElement = new TcpTransportBindingElement();
CustomBinding metaBind = new CustomBinding(metaElement);
host.Description.Behaviors.Add(new System.ServiceModel.Description.ServiceMetadataBehavior());
host.AddServiceEndpoint(typeof(System.ServiceModel.Description.IMetadataExchange), metaBind, "MEX");
}
否則在生成代理類的時(shí)候會(huì)報(bào)錯(cuò)如下的錯(cuò)誤:
源碼下載:
/Files/gaoweipeng/WCFChat.rar
欄 目:C#教程
下一篇:C#基于WebBrowser獲取cookie的實(shí)現(xiàn)方法
本文標(biāo)題:分享WCF聊天程序--WCFChat實(shí)現(xiàn)代碼
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6874.html
您可能感興趣的文章
- 01-10WPF實(shí)現(xiàn)類似360安全衛(wèi)士界面的程序源碼分享
- 01-10C#基于UDP實(shí)現(xiàn)的P2P語音聊天工具
- 01-10分享WCF文件傳輸實(shí)現(xiàn)方法---WCFFileTransfer
- 01-10C#制作簡(jiǎn)單的多人在線即時(shí)交流聊天室
- 01-10分享我在工作中遇到的多線程下導(dǎo)致RCW無法釋放的問題
- 01-10分享一個(gè)C#編寫簡(jiǎn)單的聊天程序(詳細(xì)介紹)
- 01-10Windows中使用C#為文件夾和文件編寫密碼鎖的示例分享
- 01-10C# WCF簡(jiǎn)單入門圖文教程(VS2010版)
- 01-10區(qū)分WCF與WebService的異同、優(yōu)勢(shì)
- 01-10基于C#實(shí)現(xiàn)網(wǎng)頁爬蟲


閱讀排行
- 1C語言 while語句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實(shí)現(xiàn)txt定位指定行完整實(shí)例
- 01-10WinForm實(shí)現(xiàn)仿視頻播放器左下角滾動(dòng)新
- 01-10C#停止線程的方法
- 01-10C#實(shí)現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實(shí)現(xiàn)讀取注冊(cè)表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11ajax實(shí)現(xiàn)頁面的局部加載


