激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C# - 分享WCF聊天程序--WCFChat實現代碼

分享WCF聊天程序--WCFChat實現代碼

2021-11-02 13:39C#教程網 C#

無意中在一個國外的站點下到了一個利用WCF實現聊天的程序,作者是:Nikola Paljetak。研究了一下,自己做了測試和部分修改,感覺還不錯,分享給大家

無意中在一個國外的站點下到了一個利用wcf實現聊天的程序,作者是:nikola paljetak。研究了一下,自己做了測試和部分修改,感覺還不錯,分享給大家。
先來看下運行效果:
開啟服務:
分享WCF聊天程序--WCFChat實現代碼
客戶端程序:
分享WCF聊天程序--WCFChat實現代碼
分享WCF聊天程序--WCFChat實現代碼
程序分為客戶端和服務器端:

------------服務器端:

ichatservice.cs:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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;
      }
    }
  }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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的使用方法,需要注意的是:如果想利用工具生成代理類,需要加上下面的代碼:

?
1
2
3
4
5
6
7
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");
      }

否則在生成代理類的時候會報錯如下的錯誤:

分享WCF聊天程序--WCFChat實現代碼

源碼下載:
/files/gaoweipeng/wcfchat.rar

延伸 · 閱讀

精彩推薦
  • C#C#通過KD樹進行距離最近點的查找

    C#通過KD樹進行距離最近點的查找

    這篇文章主要為大家詳細介紹了C#通過KD樹進行距離最近點的查找,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    帆帆帆6112022-01-22
  • C#C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    C# 實現對PPT文檔加密、解密及重置密碼的操作方法

    這篇文章主要介紹了C# 實現對PPT文檔加密、解密及重置密碼的操作方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下...

    E-iceblue5012022-02-12
  • C#C#裁剪,縮放,清晰度,水印處理操作示例

    C#裁剪,縮放,清晰度,水印處理操作示例

    這篇文章主要為大家詳細介紹了C#裁剪,縮放,清晰度,水印處理操作示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    吳 劍8332021-12-08
  • C#深入解析C#中的交錯數組與隱式類型的數組

    深入解析C#中的交錯數組與隱式類型的數組

    這篇文章主要介紹了深入解析C#中的交錯數組與隱式類型的數組,隱式類型的數組通常與匿名類型以及對象初始值設定項和集合初始值設定項一起使用,需要的...

    C#教程網6172021-11-09
  • C#C#實現XML文件讀取

    C#實現XML文件讀取

    這篇文章主要為大家詳細介紹了C#實現XML文件讀取的相關代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Just_for_Myself6702022-02-22
  • C#WPF 自定義雷達圖開發實例教程

    WPF 自定義雷達圖開發實例教程

    這篇文章主要介紹了WPF 自定義雷達圖開發實例教程,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下...

    WinterFish13112021-12-06
  • C#Unity3D實現虛擬按鈕控制人物移動效果

    Unity3D實現虛擬按鈕控制人物移動效果

    這篇文章主要為大家詳細介紹了Unity3D實現虛擬按鈕控制人物移動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    shenqingyu060520232410972022-03-11
  • C#C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題實例

    這篇文章主要介紹了C#設計模式之Visitor訪問者模式解決長隆歡樂世界問題,簡單描述了訪問者模式的定義并結合具體實例形式分析了C#使用訪問者模式解決長...

    GhostRider9502022-01-21
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 中午字幕无线码一区2020 | 伊人午夜视频 | 99精品在线视频观看 | 美女很黄很黄免费的 | 在线播放一区二区三区 | 久久久久久艹 | 国产午夜电影在线观看 | 美女网站黄在线观看 | 欧美日韩影视 | 国产亚洲高清视频 | 亚洲一区二区中文字幕在线观看 | 国产免费一区二区三区 | 午夜丰满少妇高清毛片1000部 | 欧美成年人视频 | 成人免费观看在线 | 欧美一级免费在线观看 | 91天堂国产在线 | 免费一级肉体全黄毛片 | 欧美黄色大片免费观看 | 免费观看一级欧美大 | 毛片在哪看 | 久久99精品久久久久久秒播蜜臀 | 国产91小视频在线观看 | 成人国产高清 | 国产精品久久久免费看 | 中午日产幕无线码1区 | 亚洲第五色综合网 | h视频在线免费看 | 97中文字幕在线观看 | av电影免费在线看 | 国产高清永久免费 | 久久一本日日摸夜夜添 | 久久久久久麻豆 | 久久精品视频1 | 午夜小视频免费观看 | 亚洲精品成人在线视频 | 婷婷久久综合九色综合色多多蜜臀 | 亚洲第一精品在线 | 亚洲电影免费观看国语版 | 91网址在线播放 | 色网站免费观看 |