一. 整體介紹
二. 從零搭建
1. 新建MVC5項目,通過Nuget安裝程序集:Microsoft.AspNet.SignalR,安裝成功后如下圖。

2. 新建一個中心模型Hub類(MySpecHub1),該類繼承了Hub類,並且幾個必要方法可以override。

3. 新建一個OWIN Startup Class(Startup),並在Configuration方法中指定使用的通訊模型的URl, 這里采用默認的方式:如: app.MapSignalR(); 【后續展開介紹如何指定URL及其中的問題】
PS: 程序啟動時候首先會找到該類,然后運行里面的Configuration方法,從而url和通訊模型的匹配將生效。

4. 引入必要的JS文件,進行前端代碼的編寫,如下圖。【后續詳細介紹】

三. 模型和URL匹配
我們都知道,在OWIN Startup Class(即Startup類)中Configuration方法中進行模型URL的指定,並且在很多例子中,看到都是這么寫:app.MapSignalR(); 貌似並沒有配置URL,但事實上並不是這樣的,我們通過反編譯代碼可以看到,它會默認指定一個路徑 "/signalr" ,如下圖:

特別注意:這里的"/signalr",與js端的自動生成代理類的代碼:<script src="~/signalr/hubs"></script>沒有任何毛線關系,這兩個根本不是一個東西!!!!!,只是路徑相似而已罷了。
那么如何指定路徑:
通過 代碼:app.MapSignalR("/myhub1", new HubConfiguration()); 可以將路徑指定為:"/myhub1",至於前端頁面怎么與之匹配,在下面介紹。
PS:這里還可以配置其它參數,如下圖:

四. 服務器端代碼介紹
前端頁面的JS代碼有兩種模式,代理模式和非代理模式(下面介紹),但無論JS使用哪種模式,服務器端的代碼都是唯一不變。
1. MySpecHub1類繼承成Hub類,所以可以Override三個方法:
(1). OnConnected:連接成功時調用
(2). OnDisconnected:連接斷開時調用
(3). OnReconnected:重連時調用
2. 自定義方法
服務器端可以自定義方法供客戶端調用,比如: public void AddUser(string userName, string userId){....}
特別特別注意:前端【代理模式】的情況下調用的服務器端方法或者與代理文件建立連接時,有一個非常坑爹的規則,首字母必須小寫,比如服務器端定義方法為:“AddUserMsg”,前端【代理模式】情況下調用必須寫成:“addUserMsg”;再比如這里的Hub類為 "MySpecHub1",前端調用的時候必須寫成"mySpecHub1";對此我表示很無語,當年這一點坑了我很久!!!
PS:上述指定是【代理模式】,【非代理模式】不存在這個問題。
雖然我們已經知道這個規則了,但經常寫着寫着就忘了,那么如何解決上面這個問題呢?:
這里有兩個特性分別是:[HubName()] 和 [HubMethodName()],可以自行指定Hub類和自定義方法的名稱,指定為什么,前端調用就用什么,這樣【代理模式】下,坑爹的首字母小寫規則,就不存在了。為了后續不麻煩,所以我通常在每個方法上面都加: [HubMethodName(nameof(方法名))],這樣就不會存在問題了,如下圖:

3. 上下文對象(this.Context)
(1). 當前用戶的標記: this.Context.ConnectionId (Guid生成,不會重復)
(2). 其它信息:RequestCookies、Headers、QueryString、User、Request
4. 如何調用客戶端方法
使用Clients對象進行調用,Clients對象下的屬性和方法有:
① 向所有人發送(包括自己):All { get; }
② 向所有人發送(排除一些人):AllExcept(params string[] excludeConnectionIds);
③ 向指定人發送,一對一:Client(string connectionId);
④ 向一些人發送,一對多:Clients(IList<string> connectionIds);
⑤ 向某個組發送(排除一些人):Group(string groupName, params string[] excludeConnectionIds);
⑥ 向多個組發送(排除一些人):Groups(IList<string> groupNames, params string[] excludeConnectionIds);
⑦ 由Id標識的特定用戶:Users(IList<string> userIds);
⑧ 由Id標識的特定多用戶:User(string userId);
調用形式比如:Clients.All.客戶端方法名稱
5. 組的概念(Groups對象)
① 將連接添加到指定組:Task Add(string connectionId, string groupName);
② 從指定組中刪除連接:Task Remove(string connectionId, string groupName);
調用如:this.Groups.Add("", "");
截圖幾段代碼:


五. 客戶端(js)代碼介紹-代理模式
1. 必備JS文件的引入
前端Html頁面使用SignalR,必須引入兩個文件:JQuery和SignalR,必須JQuery在上,且最低版本為1.6.4,不能再低了。如下圖:

2. 代理JS代碼的生成
代理JS代碼用於幫助客戶端調用服務器端自定義方法,注意這里的引入路徑只能是: <script src="/signalr/js"></script> 或者 <script src="/signalr/hubs"></script>,至於為什么路徑非要這么寫?這個地方不糾結了,我們姑且就這么使用(有興趣探討一下內部原理吧)。
引入該代碼后,進入頁面F12,會發現多了JS代碼,沒錯,這就是自動生成的代理代碼,在前端代碼的編寫中,需要依賴該文件。

可能會用人問,我把自動生成的這個JS代碼拷貝出來,單獨放到一個JS文件里,然后在頁面引入,並去掉生成代理代碼的這句話 <script src="/signalr/js"></script>,行不行呢?
答案是:肯定行。
但這種拷貝出來的方式有點Low,服務器端代碼只要一改,我就需要重新拷貝一遍,那么有沒有別的方便的方法呢?
顯然有,大約有兩種方法。
①:借助Microsoft.AspNet.SignalR.Utils程序集和指令。
②:借助Microsoft.AspNet.SignalR.Utils程序集和VS開發工具
在這一節里,暫時不介紹這兩種方式,后面章節詳細介紹。
3. 如何與服務器Hub模型路徑相配?
在上面的代碼中介紹過,服務器Hub模型默認的URL為"/signalr",那么客戶端的代碼怎么寫呢?
1 //1. 與服務器路徑進行匹配 2 var conn = $.connection.hub; 3 //2. 與生成的代理類建立連接 4 var proxy = $.connection.mySpecHub1;
乍一看,絲毫沒有看到與"/signalr"相關的代碼,不要急,這時去看一下自動生成代理類中的代碼,如下圖:

我們再看一下SignalR的JS代碼中關於hubConnection方法的聲明,如下圖:


配合第二個截圖簡單分析一下這塊源代碼,首先if判斷" 一真或為真",只要!url 和 useDefaultPath有一個是真的就進入方法體內部,然后在拼接 url+“/signalr”,如果不進入if方法體,那么你輸入的url是什么,這里用的就是什么。
前面的代碼為: signalR.hub = $.hubConnection("/signalr", { useDefaultPath: false }); ||兩邊都為false,進入不了if方法體內部,所以URL就是默認輸入的“/signalr”。
4. 坑爹的調用規范
在代理模式中,客戶端調用服務器端方法或者與代理建立連接的時候,比如:
①:服務器端的Hub名稱為MySpecHub1,客戶端調用的時候必須為首字母小寫:$.connection.mySpecHub1;
②:服務器端自定義的方法為SendSingleMsg,客戶端調用的時候必須為首字母小寫:proxy.server.sendSingleMsg;
注:非代理模式中則不存在這個問題!!!!
解決: 引入兩個特性[HubName("")] 和 [HubMethodName("")] ,放在服務器端代碼上面,就解決了。 (詳見服務器端代碼)
5. 客戶端方法的聲明和調用服務器端方法
①. 聲明客戶端方法: proxy.client.xxx = function (x1, x2) {} xxx代表客戶端方法名稱
②. 調用服務器端方法: proxy.server.xxx(x1,x2); xxx代表服務器端方法名稱
注:這里的proxy,是 $.connection.mySpecHub1; 與自動生成的代理類建立連接。


6. 服務器端指定模型URL后,前端如何匹配?
如服務器端代碼為:app.MapSignalR("/myhub1", new HubConfiguration());
①. 當使用自動生成代理類js文件時候,與<script src="/signalr/hubs"></script>沖突,暫時未解決 (歡迎下方留言討論)
②. 手動引入代理類時候可以使用,只需添加 conn.url = "/myhub1"; 即可以將路徑改為 "/myhub1"。
代碼如下:

通過Fiddler檢測一下。

7. 其它方法
同PersistentConnection模式中相同,比如建立連接和檢測斷線。


六. 客戶端(js)代碼介紹-非代理模式
有了前面代理模式的鋪墊,非代理模式就很容易了,下面介紹一下在使用上的一些區別:
1. 基本使用
不需要引入 <script src="/signalr/js"></script> 或者 <script src="/signalr/hubs"></script>,也不需要引入手動添加的代理類 <script src="~/Scripts/AutoProxy.js"></script>,但在代碼上要這么寫,比如創建代理類: $.hubConnection().CreateHubProxy("MySpecHub1");
詳細代碼如下:
1 //1. 與服務器路徑進行匹配 2 var conn = $.hubConnection(); 3 //2. 手動創建代理類 4 var proxy = conn.createHubProxy("MySpecHub1");
2. 在非代理模式中,服務器端的Hub名稱和服務器端自定義的方法不必首字母小寫(PS:小寫也能用)
①:服務器端的Hub名稱為MySpecHub1,客戶端調用的時候 conn.createHubProxy("MySpecHub1");
②:服務器端自定義的方法為SendSingleMsg,客戶端調用的時候必須為首字母小寫: proxy.invoke("SendSingleMsg", $("#j_receiveId").val(), $("#j_content").val());
注:服務器端的兩個特性[HubName("")] 和 [HubMethodName("")]仍然好用!!!
3. 聲明客戶端方法和調用服務器端方法
①. 聲明客戶端方法: proxy.on("方法名",function(x1,x2,x3){});
②. 調用服務器端方法: proxy.invoke("方法名", "參數1","參數2","參數3");


4 默認路徑匹配
在不使用代理的情況下,$.hubConnection()與服務器路徑進行匹配的時候,通過Fiddler可以發現,默認是"/signalr"路徑
5. 服務器端指定路徑模型路徑:
如服務器端代碼為:app.MapSignalR("/myhub1", new HubConfiguration());
客戶端對應的代碼為:$.hubConnection("/myhub1", { useDefaultPath: false });
注:通過Fidder或者調試源代碼,可以看到路徑已經改為"/myhub1";
特別補充:如果客戶端代碼var conn = $.hubConnection("/myhub1")這么寫,useDefaultPath這個屬性默認為true,則最后的路徑為:"/myhub1/signalr",原因借助前面的分析很容易理解了。

七. 第三方調用
上面介紹的所有代碼都是直接基於 Hub模型這個類來通信的,但在很多情況下, 發送信息之前需要進行很多其他業務的處理,這個時候所有的代碼都寫在Hub類中就不太合適了, 這時候需要借助控制器里的Action來通信,那么問題來了,如何通過調用控制器里的方法來實現發送信息的功能呢?
其實非常簡單,我們只需要通過 GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>(); 獲取到這個Hub即可。
代碼如下:
1 /// <summary> 2 /// 向所有人發送消息 3 /// </summary> 4 /// <param name="myConnectionId">當前用戶的登錄標記</param> 5 /// <param name="msg">發送的信息</param> 6 public string MySendAll(string myConnectionId ,string msg) 7 { 8 //Hub模式 9 var hub = GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>(); 10 hub.Clients.AllExcept(myConnectionId).receiveMsg($"用戶【{myConnectionId}】發來消息:{msg}"); 11 return "ok"; 12 }
八. 聊天室樣例
在本系列的第一節,基於WebSocket寫了一個聊天室樣例,還吐槽了一番,寫法很麻煩,這里基於Signalr的Hub模型,再寫一次聊天室,並補充幾個新功能。
效果圖如下:

包括的功能有:
①:連接成功后通知所有人包括自己登錄成功。
②:離線后,通知除了自己以外的所有人已經離開。
③:通過輸入接收人的ConnectionId,實現一對一發送信息。
④: 通過點擊群發按鈕,向除了自己以外的所有人發送信息。
⑤:可以進入room1或room2房間,然后實現向指定房間內的所有人發送信息。
⑥:調用控制器中中的方法進行通訊
下面分享代碼,包括(Hub模型代碼,控制器代碼,前端代碼(代理和非代理兩套))
Hub模型代碼
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using System.Web; 6 using Microsoft.AspNet.SignalR; 7 using Microsoft.AspNet.SignalR.Hubs; 8 9 namespace HubDemo 10 { 11 12 13 //[HubName("MySpecHub1")] 14 public class MySpecHub1 : Hub 15 { 16 17 /**************************************下面是Override的方法*************************************************/ 18 19 20 #region 01-連接成功的時候調用 21 /// <summary> 22 /// 連接成功的時候調用 23 /// </summary> 24 /// <returns></returns> 25 public override Task OnConnected() 26 { 27 28 //調用客戶端的方法通知所有人包括自己 29 Clients.All.LoginSuccessNotice($"用戶【{this.Context.ConnectionId}】登錄成功", this.Context.ConnectionId); 30 //回傳給客戶端自己的CId 31 Clients.Client(this.Context.ConnectionId).ReceiveOwnCid(this.Context.ConnectionId); 32 return base.OnConnected(); 33 } 34 #endregion 35 36 #region 02-連接斷開的時候調用 37 /// <summary> 38 /// 連接斷開的時候調用 39 /// </summary> 40 /// <param name="stopCalled"></param> 41 /// <returns></returns> 42 public override Task OnDisconnected(bool stopCalled) 43 { 44 //除去自己以外的消息 45 Clients.AllExcept(this.Context.ConnectionId).receiveMsg($"用戶【{this.Context.ConnectionId}】已經離開"); 46 return base.OnDisconnected(stopCalled); 47 } 48 #endregion 49 50 #region 03-重新連接的時候調用 51 /// <summary> 52 /// 重新連接的時候調用 53 /// </summary> 54 /// <returns></returns> 55 public override Task OnReconnected() 56 { 57 return base.OnReconnected(); 58 } 59 #endregion 60 61 /**************************************下面是自定義的服務器端方法*************************************************/ 62 63 #region 01-點對點發送消息 64 /// <summary> 65 /// 點對點發送消息 66 /// </summary> 67 /// <param name="receiveId"></param> 68 /// <param name="msg"></param> 69 public void SendSingleMsg(string receiveId, string msg) 70 { 71 Clients.Client(receiveId).receiveMsg($"用戶【{this.Context.ConnectionId}】發來消息:{msg}"); 72 } 73 #endregion 74 75 #region 02-群發消息 76 /// <summary> 77 /// 群發消息 78 /// </summary> 79 /// <param name="msg"></param> 80 [HubMethodName(nameof(SendAllMsg))] 81 public void SendAllMsg(string msg) 82 { 83 //除去自己以外的消息(不需要自己存儲ConnectionId) 84 Clients.AllExcept(this.Context.ConnectionId).receiveMsg($"用戶【{this.Context.ConnectionId}】發來消息:{msg}"); 85 } 86 #endregion 87 88 #region 03-進入指定組 89 /// <summary> 90 /// 進入指定組 91 /// </summary> 92 /// <param name="roomName">組的名稱</param> 93 [HubMethodName(nameof(EnterRoom))] 94 public void EnterRoom(string roomName) 95 { 96 //進入組 97 Groups.Add(this.Context.ConnectionId, roomName); 98 //告訴自己進入成功 99 Clients.Client(this.Context.ConnectionId).receiveMsg($"用戶【{this.Context.ConnectionId}】成功進入組:【{roomName}】"); 100 } 101 #endregion 102 103 #region 04-向指定組發送消息 104 /// <summary> 105 /// 向指定組發送消息 106 /// </summary> 107 /// <param name="roomName">組名</param> 108 /// <param name="msg">內容</param> 109 [HubMethodName(nameof(SendRoomNameMsg))] 110 public void SendRoomNameMsg(string roomName, string msg) 111 { 112 //向指定組發送消息,如果這個組包含自己,將自己除外 113 Clients.Group(roomName, this.Context.ConnectionId).receiveMsg($"用戶【{this.Context.ConnectionId}】發來消息:{msg}"); 114 } 115 #endregion 116 117 } 118 }
控制器代碼
1 using Microsoft.AspNet.SignalR; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 8 namespace HubDemo.Controllers 9 { 10 /// <summary> 11 /// Hub模型(中心模型) 12 /// 13 /// </summary> 14 public class HubController : Controller 15 { 16 #region 01-代理模式頁面 17 /// <summary> 18 /// 代理模式頁面 19 /// </summary> 20 /// <returns></returns> 21 public ActionResult Index() 22 { 23 return View(); 24 } 25 #endregion 26 27 #region 02-非代理模式頁面 28 /// <summary> 29 /// 非代理模式頁面 30 /// </summary> 31 /// <returns></returns> 32 public ActionResult NoProxyIndex() 33 { 34 return View(); 35 } 36 #endregion 37 38 39 40 /****************************************************下面是第三方調用方法****************************************************************/ 41 42 43 #region 01-向所有人發送消息 44 /// <summary> 45 /// 向所有人發送消息 46 /// </summary> 47 /// <param name="myConnectionId">當前用戶的登錄標記</param> 48 /// <param name="msg">發送的信息</param> 49 public string MySendAll(string myConnectionId ,string msg) 50 { 51 //Hub模式 52 var hub = GlobalHost.ConnectionManager.GetHubContext<MySpecHub1>(); 53 hub.Clients.AllExcept(myConnectionId).receiveMsg($"用戶【{myConnectionId}】發來消息:{msg}"); 54 return "ok"; 55 } 56 #endregion 57 58 59 60 } 61 }
代理
1 @{ 2 Layout = null; 3 } 4 5 <!DOCTYPE html> 6 7 <html> 8 <head> 9 10 <meta name="viewport" content="width=device-width" /> 11 <title>Index</title> 12 <script src="~/Scripts/jquery-3.3.1.min.js"></script> 13 <script src="~/Scripts/jquery.signalR-2.3.0.js"></script> 14 <!--自動生成代理類 --> 15 <script src="/signalr/hubs"></script> 16 <!--手動引入代理類 --> 17 @*<script src="~/Scripts/AutoProxy.js"></script>*@ 18 <script type="text/javascript"> 19 $(function () { 20 21 //一. 初始化信息 22 //默認路徑 23 //1. 與服務器路徑進行匹配 24 var conn = $.connection.hub; 25 //2. 與生成的代理類建立連接 26 var proxy = $.connection.mySpecHub1; 27 //3.當服務器端指定路徑的時候,需要有下面的代碼進行匹配(僅使用手動代理) 28 //conn.url = "/myhub1"; 29 30 //二. 定義客戶端的方法 31 //1 接受用戶登錄成功后的提示 32 proxy.client.LoginSuccessNotice = function (data, connectionId) { 33 $("#j_Msg").append("<li>" + data + "</li>"); 34 35 }; 36 //2 接收點對點用戶發送來的消息 37 proxy.client.receiveMsg = function (data) { 38 $("#j_Msg").append("<li>" + data + "</li>"); 39 } 40 //3. 接收自己的connectionId 41 proxy.client.ReceiveOwnCid = function (connectionId) { 42 //把用戶ConnectionID存起來 43 $("#j_connectionId").val(connectionId); 44 } 45 46 47 //三. 主動事件 48 //1.建立連接 49 $("#j_connect").click(function () { 50 conn.start().done(function () { 51 $("#j_notice").html("連接成功"); 52 }).fail(function () { 53 console.log('Could not connect'); 54 }); 55 }); 56 //2.斷開連接 57 $("#j_close").click(function () { 58 conn.stop(); 59 }); 60 //3.點對點發送消息 61 $("#j_send").click(function () { 62 var state = conn.state; 63 if (state == 1) { 64 //調用服務器端方法 65 proxy.server.sendSingleMsg($("#j_receiveId").val(), $("#j_content").val()); 66 } else if (state == 0) { 67 $("#j_notice").html("正在連接中,請稍等"); 68 } else if (state == 2) { 69 $("#j_notice").html("正在重連,請稍等"); 70 } else if (state == 4) { 71 $("#j_notice").html("掉線了,請重新連接"); 72 } 73 74 }); 75 //4.群發消息 76 $("#j_sendAll").click(function () { 77 var state = conn.state; 78 if (state == 1) { 79 //調用服務器端方法 80 proxy.server.SendAllMsg($("#j_content").val()); 81 } else if (state == 0) { 82 $("#j_notice").html("正在連接中,請稍等"); 83 } else if (state == 2) { 84 $("#j_notice").html("正在重連,請稍等"); 85 } else if (state == 4) { 86 $("#j_notice").html("掉線了,請重新連接"); 87 } 88 89 }); 90 //5.進入room1 91 $("#j_room1").click(function () { 92 //調用服務器端方法 93 proxy.server.EnterRoom("room1"); 94 }); 95 //6.進入room2 96 $("#j_room2").click(function () { 97 //調用服務器端方法 98 proxy.server.EnterRoom("room2"); 99 }); 100 //7. 給room1中的用戶發送消息 101 $("#j_sendRoom1").click(function () { 102 proxy.server.SendRoomNameMsg("room1", $('#j_content2').val()); 103 }); 104 //8. 給room2中的用戶發送消息 105 $("#j_sendRoom2").click(function () { 106 proxy.server.SendRoomNameMsg("room2", $('#j_content2').val()); 107 }); 108 109 //9. 調用控制器中的方法發送信息 110 $("#j_btn").click(function () { 111 var myConnectionId = $("#j_connectionId").val(); 112 var msg = $('#j_content3').val(); 113 console.log(myConnectionId); 114 $.post("/Hub/MySendAll", { myConnectionId: myConnectionId, msg: msg }, function (data) { 115 if (data == "ok") { 116 alert("發送成功"); 117 } 118 }); 119 120 }); 121 122 123 124 125 126 //四. 監控事件 127 //1. 連接斷開的方法 128 conn.disconnected(function () { 129 $("#j_notice").html("連接中斷"); 130 }); 131 132 133 134 135 }); 136 </script> 137 </head> 138 <body> 139 <div> 140 <div><span>提示:</span><span id="j_notice"></span></div> 141 <div style="margin-top:20px"> 142 <button id="j_connect">建立連接</button> 143 <button id="j_close">關閉連接</button> 144 </div> 145 <div style="margin-top:20px"> 146 <input type="text" value="" placeholder="請輸入接收人的標記" id="j_receiveId" /> 147 <input type="text" value="" placeholder="請輸入發送內容" id="j_content" /> 148 <button id="j_send">單發</button> 149 <button id="j_sendAll">群發</button> 150 </div> 151 <div style="margin-top:20px"> 152 <button id="j_room1">進入room1</button> 153 <button id="j_room2">進入room2</button> 154 </div> 155 <div style="margin-top:20px"> 156 <input type="text" value="" placeholder="請輸入發送內容" id="j_content2" /> 157 <button id="j_sendRoom1">給room1發送消息</button> 158 <button id="j_sendRoom2">給room2發送消息</button> 159 </div> 160 <div style="margin-top:20px"> 161 <input type="text" value="" placeholder="請輸入發送內容" id="j_content3" /> 162 <button id="j_btn">調用控制器中的方法</button> 163 </div> 164 <div> 165 <ul id="j_Msg"></ul> 166 </div> 167 </div> 168 <input type="hidden" value="" id="j_connectionId" /> 169 </body> 170 </html>
非代理
1 @{ 2 Layout = null; 3 } 4 5 <!DOCTYPE html> 6 7 <html> 8 <head> 9 10 <meta name="viewport" content="width=device-width" /> 11 <title>Index</title> 12 <script src="~/Scripts/jquery-3.3.1.min.js"></script> 13 <script src="~/Scripts/jquery.signalR-2.3.0.js"></script> 14 <script type="text/javascript"> 15 $(function () { 16 17 18 //一. 初始化信息 19 //默認路徑 20 //1. 與服務器路徑進行匹配 21 var conn = $.hubConnection(); 22 //2. 手動創建代理類 23 var proxy = conn.createHubProxy("MySpecHub1"); 24 25 26 //指定路徑 27 ////1. 與服務器路徑進行匹配 28 //var conn = $.hubConnection("/myhub1", { useDefaultPath: false }); //對應的路徑為"/myhub1" 29 //////var conn = $.hubConnection("/myhub1"); //對應的路徑為"/myhub1/signalr" 30 ////2. 手動創建代理類 31 //var proxy = conn.createHubProxy("MySpecHub1"); 32 33 34 //二. 定義客戶端的方法 35 //1 接受用戶登錄成功后的提示 36 proxy.on("LoginSuccessNotice", function (data, connectionId) { 37 $("#j_Msg").append("<li>" + data + "</li>"); 38 }); 39 //2 接收點對點用戶發送來的消息 40 proxy.on("receiveMsg", function (data) { 41 $("#j_Msg").append("<li>" + data + "</li>"); 42 }); 43 //3. 接收自己的connectionId 44 proxy.on("ReceiveOwnCid", function (connectionId) { 45 //把用戶ConnectionID存起來 46 $("#j_connectionId").val(connectionId); 47 }); 48 49 50 51 //三. 主動事件 52 //1.建立連接 53 $("#j_connect").click(function () { 54 conn.start().done(function () { 55 $("#j_notice").html("連接成功"); 56 }).fail(function () { 57 console.log('Could not connect'); 58 }); 59 }); 60 //2.斷開連接 61 $("#j_close").click(function () { 62 conn.stop(); 63 }); 64 65 //3.點對點發送消息 66 $("#j_send").click(function () { 67 var state = conn.state; 68 if (state == 1) { 69 //調用服務器端方法 70 proxy.invoke("SendSingleMsg", $("#j_receiveId").val(), $("#j_content").val()); 71 } else if (state == 0) { 72 $("#j_notice").html("正在連接中,請稍等"); 73 } else if (state == 2) { 74 $("#j_notice").html("正在重連,請稍等"); 75 } else if (state == 4) { 76 $("#j_notice").html("掉線了,請重新連接"); 77 } 78 79 }); 80 //4.群發消息 81 $("#j_sendAll").click(function () { 82 var state = conn.state; 83 if (state == 1) { 84 //調用服務器端方法 85 proxy.invoke("SendAllMsg", $("#j_content").val()); 86 } else if (state == 0) { 87 $("#j_notice").html("正在連接中,請稍等"); 88 } else if (state == 2) { 89 $("#j_notice").html("正在重連,請稍等"); 90 } else if (state == 4) { 91 $("#j_notice").html("掉線了,請重新連接"); 92 } 93 94 }); 95 96 //5.進入room1 97 $("#j_room1").click(function () { 98 //調用服務器端方法 99 proxy.invoke("EnterRoom", "room1"); 100 }); 101 //6.進入room2 102 $("#j_room2").click(function () { 103 //調用服務器端方法 104 proxy.invoke("EnterRoom", "room2"); 105 }); 106 //7. 給room1中的用戶發送消息 107 $("#j_sendRoom1").click(function () { 108 proxy.invoke("SendRoomNameMsg", "room1", $('#j_content2').val()); 109 }); 110 //8. 給room2中的用戶發送消息 111 $("#j_sendRoom2").click(function () { 112 proxy.invoke("SendRoomNameMsg", "room2", $('#j_content2').val()); 113 }); 114 115 //9. 調用控制器中的方法發送信息 116 $("#j_btn").click(function () { 117 var myConnectionId = $("#j_connectionId").val(); 118 var msg = $('#j_content3').val(); 119 console.log(myConnectionId); 120 $.post("/Hub/MySendAll", { myConnectionId: myConnectionId, msg: msg }, function (data) { 121 if (data == "ok") { 122 alert("發送成功"); 123 } 124 }); 125 126 }); 127 128 129 130 131 //四. 監控事件 132 //1. 連接斷開的方法 133 conn.disconnected(function () { 134 $("#j_notice").html("連接中斷"); 135 }); 136 137 138 //五. 其它事件 139 140 141 }); 142 </script> 143 </head> 144 <body> 145 <div> 146 <div><span>提示:</span><span id="j_notice"></span></div> 147 <div style="margin-top:20px"> 148 <button id="j_connect">建立連接</button> 149 <button id="j_close">關閉連接</button> 150 </div> 151 <div style="margin-top:20px"> 152 <input type="text" value="" placeholder="請輸入接收人的標記" id="j_receiveId" /> 153 <input type="text" value="" placeholder="請輸入發送內容" id="j_content" /> 154 <button id="j_send">單發</button> 155 <button id="j_sendAll">群發</button> 156 </div> 157 <div style="margin-top:20px"> 158 <button id="j_room1">進入room1</button> 159 <button id="j_room2">進入room2</button> 160 </div> 161 <div style="margin-top:20px"> 162 <input type="text" value="" placeholder="請輸入發送內容" id="j_content2" /> 163 <button id="j_sendRoom1">給room1發送消息</button> 164 <button id="j_sendRoom2">給room2發送消息</button> 165 </div> 166 <div style="margin-top:20px"> 167 <input type="text" value="" placeholder="請輸入發送內容" id="j_content3" /> 168 <button id="j_btn">調用控制器中的方法</button> 169 </div> 170 <div> 171 <ul id="j_Msg"></ul> 172 </div> 173 </div> 174 <input type="hidden" value="" id="j_connectionId" /> 175 </body> 176 </html>
PS:SignalR 與WebSocket原生代碼編寫的相比較,最大的改進之處有:
①:單發和群發可以對應不同的服務器端方法,不再需要自行約定規則來識別了,同樣客戶端接收也很靈活了,可以指定方法接收。
②:不需要自己創建登錄標記,默認會生成一個ConnectionId。
③:ConnectionId會自動記錄,不需要單獨聲明一個集合來存儲。
④:寫起來太爽了(^_^)。
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則將保留追究法律責任,如需代碼請在評論處留下你的郵箱
