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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 九九热精品视频在线 | www.成人精品 | 国产精品69久久 | 久操福利视频 | 一级毛片在线视频 | 日韩欧美视频一区二区三区 | 日韩视频一区二区三区四区 | 精品国产乱码久久久久久久 | 国产精品久久久免费 | 污视频在线免费 | 成年免费在线视频 | 欧美黄色一级片在线观看 | 成人三级电影网站 | 毛片视频大全 | 国产激情精品一区二区三区 | 茄子福利视频 | 一级成人毛片 | av在线影片 | 黄色大片在线免费观看 | 99成人精品视频 | 黄色片视频观看 | 91看片在线播放 | 久久久久北条麻妃免费看 | 一级一级一级一级毛片 | 亚洲午夜精品视频 | 中文字幕一区在线观看视频 | 爱操在线| 日韩黄色免费在线观看 | 亚洲国产精久久久久久久 | 久久这里只有精品1 | 国产一区二区三区四区波多野结衣 | 极品销魂一区二区三区 | 国产精品99久久久久久大便 | 国产一区二区三区在线观看视频 | 精品少妇v888av | 久久sp | 久久精品国产清自在天天线 | 国产亚洲精品yxsp | 综合网日日天干夜夜久久 | 国产精品视频免费在线观看 | 国产99久久久久 |