程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 分享WCF聊天法式--WCFChat完成代碼

分享WCF聊天法式--WCFChat完成代碼

編輯:C#入門知識

分享WCF聊天法式--WCFChat完成代碼。本站提示廣大學習愛好者:(分享WCF聊天法式--WCFChat完成代碼)文章只能為提供參考,不一定能成為您想要的結果。以下是分享WCF聊天法式--WCFChat完成代碼正文


有意中在一個國外的站點下到了一個應用WCF完成聊天的法式,作者是:Nikola Paljetak。研討了一下,本身做了測試和部門修正,感到還不錯,分享給年夜家。
先來看下運轉後果:
開啟辦事:

客戶端法式:


法式分為客戶端和辦事器端:

------------辦事器端:

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會話。雙工協議時的回調協議類型為IChatCallback接口
  [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
  public interface IChatService
  {
    [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]//----->IsOneWay = false期待辦事器完成對辦法處置;IsInitiating = true啟動Session會話,IsTerminating = false 設置辦事器發送答復後不封閉會話
    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>
  /// 雙向通訊的回調接口
  /// </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>
  /// 設定新聞的類型
  /// </summary>
  public enum MessageType { Receive, UserEnter, UserLeave, ReceiveWhisper };
  /// <summary>
  /// 界說一個本例的事宜新聞類. 創立包括有關事宜的其他有效的信息的變量,只需派生自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 辦事器為每一個客戶會話創立一個新的高低文對象。ConcurrencyMode.Multiple 異步的多線程實例
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
  public class ChatService : IChatService
  {
    private static Object syncObj = new Object();////界說一個靜態對象用於線程部分代碼塊的鎖定,用於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>();//創立一個靜態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辦法作為參數傳遞給拜托

      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類型的泛類型,通道的類型是事前辦事契約協議好的雙工機制。
        ChatEventArgs e = new ChatEventArgs();//實例化事宜新聞類ChatEventArgs
        e.msgType = MessageType.UserEnter;
        e.name = name;
        BroadcastMessage(e);
        ChatEvent += myEventHandler;
        string[] list = new string[chatters.Count]; //以下代碼前往以後進入聊天室成員的稱列表
        lock (syncObj)
        {
          chatters.Keys.CopyTo(list, 0);//將字典中記載的用戶信息復制到數組中前往。
        }
        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;//創立一個暫時拜托實例
        lock (syncObj)
        {
          chatterTo = chatters[to]; //查找成員字典中,找到要吸收者的拜托挪用
        }
        chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);//異步方法挪用吸收者的拜托挪用
      }
      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);
    }

    //回調,依據客戶端舉措告訴對應客戶端履行對應的操作
    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)
      {
        //輪回將在線的用戶播送信息
        foreach (ChatEventHandler handler in temp.GetInvocationList())
        {
          //異步方法挪用多路播送拜托的挪用列表中的ChatEventHandler 
          handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
        }
      }
    }
    //播送中線程挪用完成的回調辦法功效:消除異常多路播送拜托的挪用列表中異常對象(空對象)
    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;
      }
    }
  }
}

------------客戶端:

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>
    /// 該函數將指定的新聞發送到一個或多個窗口。此函數為指定的窗口挪用窗口法式,直到窗口法式處置完新聞再前往。 
    /// </summary>
    /// <param name="hWnd">其窗口法式將吸收新聞的窗口的句柄</param>
    /// <param name="msg">指定被發送的新聞</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);
    //當一個窗口尺度垂直轉動條發生一個轉動事宜時發送此新聞給誰人窗口,也發送給具有它的控件
    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>
    /// 銜接辦事器
    /// </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);//為完成辦事實例的對象停止初始化
      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>
    /// 毛病提醒
    /// </summary>
    private void HandleEndJoinError()
    {
      if (wfDlg.InvokeRequired)
        wfDlg.Invoke(new HandleErrorDelegate(HandleEndJoinError));
      else
      {
        wfDlg.ShowError("沒法銜接聊天室!");
        ExitChatSession();
      }
    }
    /// <summary>
    /// 登錄停止後的處置
    /// </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>
    /// 發送新聞
    /// </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>
    /// 只要選擇一個用戶時,私聊按鈕才可用
    /// </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("暗裡對" + 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>
    /// 加入運用法式時,釋放應用資本
    /// </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>
    /// 掌握私聊按鈕的可用性,只要選擇了用戶時按鈕才可用
    /// </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>
    /// 窗體封閉時,釋放應用資本
    /// </summary>
    private void ChatForm_FormClosed(object sender, FormClosedEventArgs e)
    {
      AbortProxyAndUpdateUI();
      Application.Exit();
    }
  }
}

代碼中我做了具體的講授,信任園友們完整可以看懂。代碼中的一些應用的辦法照樣值得年夜家參考進修的。這裡觸及到了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");
      }

不然在生成署理類的時刻會報錯以下的毛病:

源碼下載:
/Files/gaoweipeng/WCFChat.rar

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved