第三節:SignalR之PersistentConnection模型詳解(步驟、用法、分組、跨域)

来源:https://www.cnblogs.com/yaopengfei/archive/2018/07/10/9284101.html
-Advertisement-
Play Games

一. 承上聲明 在上一個章節里,啰里啰嗦寫了一堆關於介紹SignalR的“廢話”,從這一篇開始往後正式擼代碼,這期間不少人(包括 張善友大哥)建議我直接用.Net Core下的SignalR,關於此簡單說一下,雖然我們要跟上時代步伐,但目前絕多數.Net項目都是基於 .Net FrameWork下的 ...


一. 承上聲明

  在上一個章節里,啰里啰嗦寫了一堆關於介紹SignalR的“廢話”,從這一篇開始往後正式擼代碼,這期間不少人(包括 張善友大哥)建議我直接用.Net Core下的SignalR,關於此簡單說一下,雖然我們要跟上時代步伐,但目前絕多數.Net項目都是基於 .Net FrameWork下的而非 .Net Core, 並且做事要有始有終,既然打算寫這個系列,就不能半途而廢,這個.Net FrameWork下的SignalR系列務必要寫完。   還有一點,不怕笑話,.Net Core雖然我也有研究,但並沒有多麼深入,暫時就不出來獻醜了,後面等熟悉了,再來補充.Net Core下的SignalR的用法。   這一節的主要內容: PersistentConnection模型 從零開始搭建的步驟、Web瀏覽器端和C#伺服器端核心方法的使用介紹、分組的概念、開啟跨域的兩種方式。   這一節的不足:沒有體現SignalR的生命周期、沒有斷線重連的合理處理、沒有心跳檢測。

幾點介紹:   

  1. PersistentConnection(永久連接)相對於Hubs模式,更加偏向底層,它的編程模式與WebSocket的寫法很類似,固定方法發送和接受,不能向Hub模式那樣 客戶端和服務端相互調用各自定義的方法。

  2. 該模型主要用於:單個發件人、分組、廣播消息的簡單終結點。

二. 從零開始搭建

1. 新建MVC5項目,通過Nuget安裝:Microsoft.AspNet.SignalR程式集,安裝成功後如下圖:

 

2. 新建一個永久連接模型類(MyPresitentConnection1),該類繼承了PersistentConnection,並且override幾個必要方法。

  

3. 新建一個OWIN Startup Class(Startup),併在Configuration方法中指定使用的通訊模型的URl,  如: app.MapSignalR<MyPresitentConnection1>("/myPreConnection1");  

  PS: 程式啟動時候首先會找到該類,然後運行裡面的Configuration方法,從而url和通訊模型的匹配將生效。

      

4. 在前端頁面中書寫SignalR的代碼,與伺服器端MyPresitentConnection1類進行連接,實現相應的通訊業務。

     

 

三. 核心方法介紹

1. 伺服器端代碼

(1). OWIN Startup Class即Startup中要配置url和通訊模型向匹配,這裡的url在web前端頁面的js中要使用,代碼如下:

1   public class Startup
2     {
3         public void Configuration(IAppBuilder app)
4         {
5             // 有關如何配置應用程式的詳細信息,請訪問 https://go.microsoft.com/fwlink/?LinkID=316888
6             //1. 基本用法的配置
7             app.MapSignalR<MyPresitentConnection1>("/myPreConnection1");
8         }
9     }

(2). 永久連接模型類MyPresitentConnection1繼承了PersistentConnection,並且可以override幾個方法。

A. PersistentConnection中可以override的幾個主要方法有:

  ①. OnConnected :連接成功後調用

  ②. OnReceived:接收到請求的時候調用

  ③. OnDisconnected:連接中斷的時候調用

  ④. OnReconnected:連接超時重新連接的時候調用

B. 核心業務主要使用PersistentConnection類中的Connection屬性,有兩個核心方法

  ①. 1對1發送消息: public static Task Send(string connectionId, object value);

  ②. 1對多發送消息: public static Task Send(IList<string> connectionIds, object value);

  ③. 廣播(群發,可以去掉不發送的人): public static Task Broadcast(object value, params string[] excludeConnectionIds);

PS:發現每個override里都有一個參數connectionId,它代表,每個客戶端連接伺服器成功後都會產生一個標記,這個標記是GUID產生的,它是唯一的, 不會重覆, 在業務中可以通過該標記connectionId來區分客戶端。

  下麵我的代碼中書寫的業務為:

  ①. OnConnected方法即連接成功後調用的方法,調用Send方法告訴自己登錄成功(當然你也可以根據實際業務告訴指定的人)。

  ②. OnReceived方法即接受請求的方法,調用Send方法向指定人一對一發送消息。

  ③. OnDisconnected方法即連接中斷的方法,調用Broadcast方法向所有人發送消息,某某已經退出。

  ④. OnReconnected方法即超時重新連接方法,執行重連業務。

分享代碼:

 1  public class TempData
 2     {
 3         /// <summary>
 4         /// 接收人的connectionId
 5         /// </summary>
 6         public string receiveId { get; set; }
 7         
 8         /// <summary>
 9         /// 發送內容
10         /// </summary>
11         public string msg { get; set; }
12     }
View Code

 

 1  public class MyPresitentConnection1 : PersistentConnection
 2     {
 3         //下麵的兩個方法OnConnected 和 OnReceived預設帶的
 4 
 5         /// <summary>
 6         /// 連接成功後的方法
 7         /// </summary>
 8         /// <param name="request"></param>
 9         /// <param name="connectionId"></param>
10         /// <returns></returns>
11         protected override Task OnConnected(IRequest request, string connectionId)
12         {
13             //Send方法,向指定人發送消息
14             return Connection.Send(connectionId, $"用戶:{connectionId}登錄成功");
15         }
16 
17         /// <summary>
18         /// 接收請求的方法
19         /// </summary>
20         /// <param name="request"></param>
21         /// <param name="connectionId"></param>
22         /// <param name="data"></param>
23         /// <returns></returns>
24         protected override Task OnReceived(IRequest request, string connectionId, string data)
25         {
26             //一對一發送消息
27             //data是一個json對象 { receiveId: $("#j_receiveId").val(), msg: $("#j_content").val() }
28             var model = JsonConvert.DeserializeObject<TempData>(data);
29 
30             return Connection.Send(model.receiveId, model.msg);
31         }
32 
33         /// <summary>
34         /// 連接中斷調用方法
35         /// </summary>
36         /// <param name="request"></param>
37         /// <param name="connectionId"></param>
38         /// <param name="stopCalled"></param>
39         /// <returns></returns>
40         protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
41         {
42             //告訴所有人該用戶退出了(包括自己,也可以配置排除一些用戶)
43             Connection.Broadcast( $"有用戶{connectionId}已經退出");
44             return base.OnDisconnected(request, connectionId, stopCalled);
45         }
46 
47         /// <summary>
48         /// 當連接在超時後重新連接時調用該方法
49         /// </summary>
50         /// <param name="request"></param>
51         /// <param name="connectionId"></param>
52         /// <returns></returns>
53         protected override Task OnReconnected(IRequest request, string connectionId)
54         {
55             return base.OnReconnected(request, connectionId);
56         }
57     }
View Code

2. 前端Html頁面

(1). 引入JS庫,這裡包括JQuery庫和SignalR庫(JQuery最低版本為1.6.4)。

 

(2). 配置路徑:$.connection("/myPreConnection1");需要與Startup中的對應

(3). 常用的幾個方法有:

  ① start:開啟連接

  ② received:接受伺服器發送來的消息

  ③ disconnected:連接中斷時調用

  ④ error:連接發生錯誤的時嗲用

  ④ stop:斷開連接

  ⑤ send:發送消息

另外還有:connectionSlow、stateChanged、reconnecting、reconnected等等

(4). 當前連接狀態有4種

  connecting: 0(正在連接),     connected: 1(正常連接,連接成功中),   reconnecting: 2(正在重連),      disconnected: 4 (掉線了)

PS: 以上代碼和WebSocket確實很像,下圖為WebSocket相關方法。

 

(5). 下麵我的代碼中的業務

分享代碼:

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     @*
10          Web客戶端用法說明
11          1. 配置路徑:$.connection("/myPreConnection1");需要與Startup中的對應
12          2. 常用的幾個方法有:
13             ① start:開啟連接
14             ② received:接受伺服器發送來的消息
15             ③ disconnected:連接中斷時調用
16             ④ error:連接發生錯誤的時嗲用
17             ④ stop:斷開連接
18             ⑤ send:發送消息
19          另外還有:connectionSlow、stateChanged、reconnecting、reconnected等等
20         3. 當前連接狀態有4種
21          connecting: 0(正在連接),   connected: 1(正常連接),  reconnecting: 2(正在重連),    disconnected: 4 (掉線了)
22     *@
23     <meta name="viewport" content="width=device-width" />
24     <title>Index</title>
25     <script src="~/Scripts/jquery-3.3.1.min.js"></script>
26     <script src="~/Scripts/jquery.signalR-2.3.0.js"></script>
27     <script type="text/javascript">
28         $(function () {
29             var conn = $.connection("/myPreConnection1");
30             //一. 監控
31             //1. 接受伺服器發來的消息
32             conn.received(function (data) {
33                 $("#j_Msg").append("<li>" + data + "</li>");
34             });
35             //2. 連接斷開的方法
36             conn.disconnected(function () {
37                 $("#j_notice").html("連接中斷");
38             });
39             //3. 連接發生錯誤時候觸發
40             conn.error(function (data) {
41                 $("#j_notice").html(data);
42             });
43             //二. 主動事件
44             //1.建立連接
45             $("#j_connect").click(function () {
46                 conn.start(function () {
47                     $("#j_notice").html("連接成功");
48                 });
49             });
50             //2.斷開連接
51             $("#j_close").click(function () {
52                 conn.stop();
53             });
54             //3.發送消息
55             $("#j_send").click(function () {
56                 //發送消息之前要判斷連接狀態,conn.state有4中狀態
57                 //connecting: 0(正在連接),   connected: 1(正常連接),  reconnecting: 2(正在重連),    disconnected: 4 (掉線了)
58                 console.log(conn.state);
59                 if (conn.state == 1) {
60                     conn.send({ receiveId: $("#j_receiveId").val(), msg: $("#j_content").val() });
61 
62                 } else if (conn.state == 0) {
63                     $("#j_notice").html("正在連接中,請稍等");
64                 } else if (conn.state == 2) {
65                     $("#j_notice").html("正在重連,請稍等");
66                 } else if (conn.state == 4) {
67                     $("#j_notice").html("掉線了,請重新連接");
68                 }
69 
70             });
71 
72         });
73     </script>
74 </head>
75 <body>
76     <div>
77         <div><span>提示:</span><span id="j_notice"></span></div>
78         <div style="margin-top:20px">
79             <button id="j_connect">建立連接</button>
80             <button id="j_close">關閉連接</button>
81         </div>
82         <div style="margin-top:20px">
83             <input type="text" value="" placeholder="請輸入接收人的標記" id="j_receiveId" />
84             <input type="text" value="" placeholder="請輸入發送內容" id="j_content" />
85             <button id="j_send">發送消息</button>
86         </div>
87         <div>
88             <ul id="j_Msg"></ul>
89         </div>
90     </div>
91 </body>
92 </html>
View Code

(6). 運行效果

 

四. 分組的概念

1. PersistentConnection類中提供了一個 IConnectionGroupManager Groups的概念,即可以將不同用戶分到不同組裡,就好比QQ的中的討論組, 在這個組裡發信息,該組裡的所有人都能看到,但別的組是看不到的。並提供了兩個方法分別是

  ①. 加入組:Task Add(string connectionId, string groupName)

  ②. 移除組:Task Remove(string connectionId, string groupName)

IConnectionGroupManager下提供兩個針對組進行發送消息的方法

  ①. 針對單個組(可以去掉不發送的人):Task Send(string groupName, object value, params string[] excludeConnectionIds);

  ②. 針對多個組(可以去掉不發送的人):Task Send(IList<string> groupNames, object value, params string[] excludeConnectionIds);

註:一個客戶端可以同時加入多個組的,就好比qq,一個用戶你可以同時在多個討論組裡討論,相互不影響。

2. 需求背景:

  有兩個房間,分別是room1和room2,將2個人加入到room1里,2兩個人加入到room2里,1個既加入room1且加入room2,測試向指定組發送消息和普通的群發消息。

測試頁面如下圖:

3. 先貼代碼後分析

實體類代碼

 1   public class RoomData
 2     {
 3         /// <summary>
 4         /// 房間名稱
 5         /// </summary>
 6         public string roomName { get; set; }
 7 
 8         /// <summary>
 9         /// 發送的消息
10         /// </summary>
11         public string msg { get; set; }
12 
13         /// <summary>
14         /// 用來區分是進入房間,還是普通的發送消息
15         /// "enter":表示進入房間
16         /// "sendRoom":表示向某個組發送信息
17         /// "":表示普通的消息發送,不區分組的概念
18         /// </summary>
19         public string action { get; set; }
20     }
View Code

伺服器端代碼

 1  public class MyPresitentConnection2 : PersistentConnection
 2     {
 3         protected override Task OnConnected(IRequest request, string connectionId)
 4         {
 5             //提示自己進入成功
 6             return Connection.Send(connectionId, "Welcome!");
 7         }
 8 
 9         protected override Task OnReceived(IRequest request, string connectionId, string data)
10         {
11             //data是一個json對象 { roomName: "room2", action: "enter", msg: "" }
12             var model = JsonConvert.DeserializeObject<RoomData>(data);
13             if (model.action == "enter")
14             {
15                 //表示建立組關係
16                 this.Groups.Add(connectionId, model.roomName);
17                 //提示自己進入房間成功
18                 Connection.Send(connectionId, $"進入{model.roomName}房間成功");
19                 //向該組中除了當前人外,均發送歡迎消息
20                 return this.Groups.Send(model.roomName, $"歡迎{connectionId}進入{model.roomName}房間", connectionId);
21             }
22             else if (model.action == "sendRoom")
23             {
24                 //表示普通的按組發送信息(除了自己以外)
25                 return this.Groups.Send(model.roomName, string.Format("用戶 {0} 發來消息: {1}", connectionId, model.msg), connectionId);
26             }
27             else
28             {
29                 //表示普通的群發,不分組
30                 return Connection.Broadcast(string.Format("用戶 {0} 發來消息: {1}", connectionId, model.msg), connectionId);
31             }
32         }
33     }
View Code

Html代碼

 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>Index</title>
11     <script src="~/Scripts/jquery-3.3.1.min.js"></script>
12     <script src
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 12、函數: 函數的功能: 定義:在真實的項目開發過程中,有些代碼會重覆利用,我們可以把它提出來,做成公共的代碼,供團隊來使用,這個我們封裝的代碼段,就是函數(功能)。 優點: 1、提高代碼的利用率。 2、減少開發時間。 3、減少代碼冗餘。 4、可維護性提高。 5、方便調試代碼。 函數的定義格式: ...
  • jdk目錄相關介紹: bin:存放的是java的相關開發工具 db:顧名思義jre附帶的輕量級資料庫 include:存放的是調用系統資源的介面文件 jre:java的運行環境 lib:核心的類庫 src.zip:java的開源代碼 JVM:指的是java虛擬機(作用:解釋class文件並且通知系統 ...
  • 參考了網路上各路大神的實現方法。主要使用了io.h庫 #include <iostream> #include <iostream> #include <cstring> #include <cstring> #include <io.h> #include <io.h> using namespa ...
  • 早在2014年oracle發佈了jdk 8,在裡面增加了lambda模塊。於是java程式員們又多了一種新的編程方式:函數式編程,也就是lambda表達式。我自己用lambda表達式也差不多快4年了,但在工作中卻鮮有看到同事使用這種編程方式,即使有些使用了,但感覺好像對其特性也不是很瞭解。我看了一上 ...
  • Valid Anagram 題目 思路與解答 答案 Valid Palindrome 題目 思路與解答 答案 Valid Palindrome II 題目 思路與解答 答案 Valid Parentheses 題目 思路與解答 答案 Valid Perfect Square 題目 思路與解答 答案 ...
  • Web的暴力破解通常用於網站的登錄視窗,其他的地方當然也可以用。比如某個網站需要驗證身份證ID時,可以在字典中批量的寫入合法的身份證ID,然後輸入頁面測試。這種方法手工都能做,只要有足夠的耐心。 ...
  • #!/usr/bin/env python #Python 3.7.0 元祖常用方法 __author__ = "lrtao2010" #元祖和列表類似,只不過元祖一旦被創建一級元素不可更改(增刪改)。 # a = ('123',['abc'],'b',345,['6','7','d'],) # a... ...
  • 本篇介紹日誌的使用,包括系統預設的控制台日誌和第三方NLog日誌管理。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...