ASP.NET Core SignalR 是微軟開發的一套基於ASP.NET Core的與Web進行實時交互的類庫,它使我們的應用能夠實時的把數據推送給Web客戶端。
功能
-
自動管理連接
-
允許同時廣播到所有客戶端
-
也可以廣播到指定的組或者特定的客戶端
-
在Github上開源,傳送門(https://github.com/aspnet/signalr)
SignalR 提供了多種連接方式,在現代化應用中,WebSocket是最佳的傳輸協議,在客戶端無法實現WebSocket協議的時候,SignalR就會采取其他方式,比如Server-Sent或者長輪詢(在ws未出現之前,我們討論的推拉模式)
中心 Hubs
SignalR是采用中心客戶端和服務器進行通訊。
中心是一種高級的管道,允許客戶端和服務器之間相互調用方法。
中心通過強類型參數傳遞給方法,進行模型綁定
Hubs.Clients
Clients屬性包含了所有的客戶端連接信息,它包含了3個屬性:
-
All 所有客戶端
-
Caller 進行此次請求的客戶端
-
Others 排除此次請求客戶端的其他客戶端
包含了多個方法:
-
= AllExcept 在指定的連接除外的所有連接的客戶端上調用方法
-
Client 在特定連接的客戶端上調用方法
-
Clients 在特定連接的客戶端上調用方法
-
Group 調用指定的組中的一種對所有連接方法
-
GroupExcept 調用中指定的組,除非指定連接到的所有連接的方法
-
Groups 調用一種對多個組的連接方法
-
OthersInGroup 調用一種對一組的連接,不包括客戶端調用 hub 方法方法
-
User 調用一種對與特定用戶關聯的所有連接方法
-
Users 調用一種對與指定的用戶相關聯的所有連接方法
每個屬性和方法返回的對象都包含一個SendAsync方法,可以對客戶端進行調用。
HubContext
可以在應用其他地方通過使用IHubContext,達到調用Hub的目的。
兩種協議
-
文本協議:JSON
-
二進制協議:MessagePack(https://msgpack.org/)
MessagePack類似於JSON,但傳輸比JSON更快,數據大小比JSON更小
服務器事項
-
創建的Hub必須繼承Microsoft.AspNetCore.SignalR.Hub,Hub類已經包含了管理連接、組和發送接收消息的屬性及事件
-
在Hub中使用的方法應該盡量使用異步的方式,因為SignalR在發送和接收消息的時候使用的是異步方法。
-
在Startup.ConfigureServices中通過services.AddSignalR對SignalR進行注冊
-
在Startup.Configure中通過app.UseSignalR方法對Hub路由進行配置
代碼解析
微軟官方示范(https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-2.1&tabs=visual-studio)中的ChatHub:
using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRChat.Hubs { public class ChatHub : Hub { //服務端方法 public async Task SendMessage(string user, string message) { //ReceiveMessage 為客戶端方法,讓所有客戶端調用這個方法 await Clients.All.SendAsync("ReceiveMessage", user, message); } } }
上述代碼為當收到客戶端發來的SendMessage請求后(發送聊天信息),我們把消息發送到所有客戶端,讓他們調用自身的ReceiveMessage方法。
用戶標識
通常情況下,在用戶進行連接后,Connection會保存用戶的用戶標識,以便對特定用戶進行發送消息。
可以實現IUserIdProvider來自定義獲取用戶的方法,例如:
public class CustomUserIdProvider : IUserIdProvider { public virtual string GetUserId(HubConnectionContext connection) { return connection.User?.FindFirst(ClaimTypes.Email)?.Value; } }
在Startup.ConfigureServices中注冊:
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
Client的方法
T All { get; }
相當於持久連接中的 Broadcast。
T AllExcept(params string[] excludeConnectionIds);
給排除本人所有人發送消息。
T Client(string connectionId);
跟Send操作就是一樣的了。
T Clients(IList<string> connectionIds);
和Send操作的重載方法一樣,可以給一批指定的人發送。
T Group(string groupName, params string[] excludeConnectionIds);
給房間中的指定人發送消息: Clients.Group("room1", "asdfasdfads");
T Groups(IList<string> groupNames, params string[] excludeConnectionIds);
給房間列表中的指定人發送消息; 【天然的聊天室功能】
T User(string userId);
這個和Client是有區別的。 這個userId => this.Context.Request.User.Identity.Name 【form驗證】
cookie中間件來做到singlar的身份驗證。
userId 是你自己定義的一個標識。
T Users(IList<string> userIds);
客戶端JS使用方法
<script src="~/lib/signalr/signalr.js"></script> <script type="text/javascript"> const connection = new signalR.HubConnectionBuilder() .withUrl("/myChatHub") .configureLogging(signalR.LogLevel.Information) .build(); connection.start().catch(err => console.error(err.toString())); //定義方法使用connection.on方法來接收返回數據 connection.on("SendMessage", (user, message) => { const encodedMsg = user + " 說:" + message; const li = document.createElement("li"); li.textContent = encodedMsg; document.getElementById("messagesList").appendChild(li); }); document.getElementById("sendBtn").addEventListener("click", function () { var user = document.getElementById('userName').value; var message = document.getElementById('message').value; //從客戶端中調用在此調用之前在自定義Hub定義的SendMessage方法 connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString())); document.getElementById('message').value = ""; }); </script>