一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

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

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

服務器之家 - 編程語言 - ASP.NET教程 - .NET實現WebSocket服務端即時通信實例

.NET實現WebSocket服務端即時通信實例

2020-04-21 13:05Tsong Chen ASP.NET教程

本篇文章主要介紹了.NET實現即時通信,WebSocket服務端實例 ,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

即時通信常用手段

1.第三方平臺 谷歌、騰訊 環信等多如牛毛,其中谷歌即時通信是免費的,但免費就是免費的并不好用。其他的一些第三方一般收費的,使用要則限流(1s/限制x條消息)要么則限制用戶數。

但穩定性什么都還不錯,又能將服務壓力甩出

2.System.Net.Sockets.Socket,也能寫一套較好的服務器端。在.NET 4.5之前用較多,使用起來麻煩。需要對數據包進行解析等操作(但貌似網上有對超長包的處理方法)

3.System.Net.WebSockets.WebSocket,這個,是.NET 4.5出來的東西,對服務器環境也有所要求,IIS8及以上。意味著Windows Server2008R2自帶的IIS不支持,Windows8及Server2012以上自帶的IIS可以。本文主要將這種方式的實例

完整流程

1).客戶端請求連接

 

復制代碼 代碼如下:

ws = new WebSocket('ws://' + window.location.hostname + ':' + window.location.port + '/Handler1.ashx?user=' + $("#user").val());

 

2).服務端獲取連接對象并存儲到連接池中

?
1
CONNECT_POOL.Add(user, socket);

3).連接對象開始監聽(每個客戶端與服務器保存長鏈接)

 

復制代碼 代碼如下:

WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

 

4).客戶端A發送消息給B

?
1
ws.send($("#to").val() + "|" + $('#content').val());

5).服務端A的連接對象監聽到來自A的消息

?
1
string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);

6).解析消息體(B|你好我是A)得到接收者ID,根據接收者ID到連接池中查找B的服務端連接對象,并通過B的連接對象將消息推送給B客戶端

?
1
2
3
WebSocket destSocket = CONNECT_POOL[descUser];
 
await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);

7).服務端A連接對象繼續監聽

 

復制代碼 代碼如下:

WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);

 

8).B客戶端接收到推送過來的消息

?
1
2
3
4
5
ws.onmessage = function (evt) {
 
  $('#msg').append('<p>' + evt.data + '</p>');
 
}

下面則是完整代碼

 客戶端部分

客戶端異常簡單,正常情況直接用WebSocket,然后監聽WebSocket的幾個事件就ok。連接的時候可將當前連接者的ID傳入(用戶編號),發送消息的時候 采用 “接收者ID|我是消息內容” 這種方式,如“A|A你好,我是B!”

但如用移動端使用還是有一些常見的場景需要處理下的

1:手機關屏幕,IOS關掉屏幕的時候WebSocket會立即失去連接,Android則會等待一段時間才會失去連接。服務器端能檢測到失去連接

2:網絡不穩定,斷網情況WebSocket也不會立即失去連接,服務器端不能知道。(可以服務端設計心跳機制,定時給連接池中的用戶發送消息,來檢測用戶是否保持連接)

3:其他等等...(突然關機、后臺結束應用)

無論哪種,客戶端在發送消息(或者網絡恢復連接、亮屏)的時候可以先判斷ws的狀態,如果不是連接狀態則需要重連(new下即可)

?
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
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
 <title></title>
 <script src="jquery-1.11.3.min.js"></script>
 <script>
 var ws;
 $().ready(function () {
  $('#conn').click(function () {
  ws = new WebSocket('ws://' + window.location.hostname + ':' + window.location.port + '/Handler1.ashx?user=' + $("#user").val());
  $('#msg').append('<p>正在連接</p>');
 
  ws.onopen = function () {
   $('#msg').append('<p>已經連接</p>');
  }
  ws.onmessage = function (evt) {
   $('#msg').append('<p>' + evt.data + '</p>');
  }
  ws.onerror = function (evt) {
   $('#msg').append('<p>' + JSON.stringify(evt) + '</p>');
  }
  ws.onclose = function () {
   $('#msg').append('<p>已經關閉</p>');
  }
  });
 
  $('#close').click(function () {
  ws.close();
  });
 
  $('#send').click(function () {
  if (ws.readyState == WebSocket.OPEN) {
   ws.send($("#to").val() + "|" + $('#content').val());
  }
  else {
   $('#tips').text('連接已經關閉');
  }
  });
 
 });
 </script>
</head>
<body>
 <div>
 <input id="user" type="text" />
 <input id="conn" type="button" value="連接" />
 <input id="close" type="button" value="關閉"/><br />
 <span id="tips"></span>
 <input id="content" type="text" />
 <input id="send" type="button" value="發送"/><br />
 <input id="to" type="text" />目的用戶
 <div id="msg">
 </div>
 </div>
</body>
</html>

服務器端部分

服務器端使用Handler(也可用WebAPI)來做,主要用WebSocket的類來實現。代碼中都有相對詳細的注釋,這邊只說一些需要注意的問題

1:Dictionary<string,WebSocket> CONNECT_POOL:用戶連接池。請求Handler的時候會將當前連接者的用戶ID傳入,服務器端維護著所有已連接的用戶ID和當前用戶的WebSocket連接對象

2:Dictionary<string,List<MessageInfo>> MESSAGE_POOL:離線消息池。如果A->B發送消息,B當前因為某種原因沒在線(突然斷網/黑屏等原因),會將這條消息先保存起來(2天),待B連接后立馬將B的離線消息推送給他。(2:MessageInfo:離線Entity。記錄當前離線消息的時間、內容)

?
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
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;
 
namespace WebApplication1
{
 /// <summary>
 /// 離線消息
 /// </summary>
 public class MessageInfo
 {
 public MessageInfo(DateTime _MsgTime, ArraySegment<byte> _MsgContent)
 {
  MsgTime = _MsgTime;
  MsgContent = _MsgContent;
 }
 public DateTime MsgTime { get; set; }
 public ArraySegment<byte> MsgContent { get; set; }
 }
 
 /// <summary>
 /// Handler1 的摘要說明
 /// </summary>
 public class Handler1 : IHttpHandler
 {
 private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();//用戶連接池
 private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();//離線消息池
 
 public void ProcessRequest(HttpContext context)
 {
  if (context.IsWebSocketRequest)
  {
  context.AcceptWebSocketRequest(ProcessChat);
  }
 }
 
 private async Task ProcessChat(AspNetWebSocketContext context)
 {
  WebSocket socket = context.WebSocket;
  string user = context.QueryString["user"].ToString();
 
  try
  {
  #region 用戶添加連接池
  //第一次open時,添加到連接池中
  if (!CONNECT_POOL.ContainsKey(user))
   CONNECT_POOL.Add(user, socket);//不存在,添加
  else
   if (socket != CONNECT_POOL[user])//當前對象不一致,更新
   CONNECT_POOL[user] = socket;
  #endregion
 
  #region 離線消息處理
  if (MESSAGE_POOL.ContainsKey(user))
  {
   List<MessageInfo> msgs = MESSAGE_POOL[user];
   foreach (MessageInfo item in msgs)
   {
   await socket.SendAsync(item.MsgContent, WebSocketMessageType.Text, true, CancellationToken.None);
   }
   MESSAGE_POOL.Remove(user);//移除離線消息
  }
  #endregion
 
  string descUser = string.Empty;//目的用戶
  while (true)
  {
   if (socket.State == WebSocketState.Open)
   {
   ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
   WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);
   
   #region 消息處理(字符截取、消息轉發)
   try
   {
    #region 關閉Socket處理,刪除連接池
    if (socket.State != WebSocketState.Open)//連接關閉
    {
    if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//刪除連接池
    break;
    }
    #endregion
 
    string userMsg = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);//發送過來的消息
    string[] msgList = userMsg.Split('|');
    if (msgList.Length == 2)
    {
    if (msgList[0].Trim().Length > 0)
     descUser = msgList[0].Trim();//記錄消息目的用戶
    buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(msgList[1]));
    }
    else
    buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMsg));
 
    if (CONNECT_POOL.ContainsKey(descUser))//判斷客戶端是否在線
    {
    WebSocket destSocket = CONNECT_POOL[descUser];//目的客戶端
    if (destSocket != null && destSocket.State == WebSocketState.Open)
     await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
    }
    else
    {
    Task.Run(() =>
    {
     if (!MESSAGE_POOL.ContainsKey(descUser))//將用戶添加至離線消息池中
     MESSAGE_POOL.Add(descUser, new List<MessageInfo>());
     MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加離線消息
    });
    }
   }
   catch (Exception exs)
   {
    //消息轉發異常處理,本次消息忽略 繼續監聽接下來的消息
   }
   #endregion
   }
   else
   {
   break;
   }
  }//while end
  }
  catch (Exception ex)
  {
  //整體異常處理
  if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);
  }
 }
 
 public bool IsReusable
 {
  get
  {
  return false;
  }
 }
 }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/Tsong/p/6385585.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩精品成人 | 色图图片 | 日本又黄又裸一级大黄裸片 | 我们日本在线观看免费动漫下载 | 国产精品怡红院永久免费 | 国产成人精品午夜免费 | 亚洲美色综合天天久久综合精品 | 91精品国产综合久 | 韩日视频在线观看 | 国产一区风间由美在线观看 | 性bbbbwwbbbb| 欧美娇小性xxxx | 国产精品久久免费观看 | 国产欧美久久久精品影院 | 奇米精品| 2019国内自拍| 精品欧美一区二区精品久久 | 国产在线三级 | 久久精品国产清白在天天线 | 玩高中女同桌肉色短丝袜脚文 | 999久久久 | 国产一区二区三区高清 | 亚洲社区在线观看 | 黑人女性猛交xxxxxⅹxx | 久久久久久88色偷偷 | 国产精品亚洲片在线观看麻豆 | 边摸边吃奶边做爽视频免费 | 亚洲欧美久久婷婷爱综合一区天堂 | 国产在线观看精品 | 国产精品香蕉一区二区三区 | 国产精品久久久免费视频 | 91麻豆精东果冻天美传媒老狼 | 农村老少伦小说 | 国产成人亚洲综合网站不卡 | 色综合久久天天综合 | 九九大香尹人视频免费 | 午夜成私人影院在线观看 | 母乳在线 | 日本中文字幕在线视频站 | 91香蕉视频在线播放 | va在线视频 |