我一直覺得學習的最好方法就是先讓程序能夠正常運行,才去學習他的原理,剖析他的細節.
就好像這個圖:
所以,我們先跟着官方文檔,創建一個 SignalR 應用: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/signalr?view=aspnetcore-2.2&tabs=visual-studio
這個例子一共涉及到下面幾個步驟:
- 自定義中心 ChatHub ;
- 在啟動類 Startup 中啟用 SignalR 服務,並添加路由;
- 編寫客戶端JS
- 下載 SignalR 官方JS.
自定義中心 : ChatHub
public class ChatHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } }
"Hub" 一詞,有的地方翻譯成"集線器",有的地方翻譯成"中心",對於我這種非科班出身的人來說,還是"中心"聽起簡單點.
對於基類 Hub ,xml 是這樣說的 : A base class for a SignalR hub. SignalR 中心的基類.
這個很簡單,看這些名字就知道他們是干嘛用的,具體的描述可以看官方文檔.
中心的作用可以這樣簡單的描述:
通過中心,我們能在服務器的代碼中定義客戶端可以調用的方法(必須是 public);
通過中心,我們能在客戶端的代碼中定義服務器可以調用的方法.
上述代碼,我們通過繼承 Hub ,定義了一個自己的中心 : ChatHub (聊天中心) ,在這個類里面,我們做了下面兩件事:
- 定義了客戶端可以調用的方法 : SendMessage(string user, string message)
- 調用了所有連接上 Hub 的客戶端的 ReceiveMessage 方法,並將 user,message 兩個字符串作為入參傳入該方法.
中心定義好了,肯定需要啟用
在啟動類 Startup 中啟用 SignalR 服務,並添加路由
public void ConfigureServices(IServiceCollection services) { ...... //注冊 SignalR 服務 services.AddSignalR(); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { ...... //設置 SignalR 中心路由 app.UseSignalR(routes => { routes.MapHub<ChatHub>("/chatHub"); }); app.UseMvc(); }
編寫客戶端JS
"use strict";//不太明白這句話是什么意思... //創建一個連接到我們創建的 ChatHub 的 connection. var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build(); //定義客戶端的方法,方法名: ReceiveMessage ,兩個入參. //注意,這個方法就是服務器要調用的方法,服務器和客戶端的名字一定要一樣. connection.on("ReceiveMessage", function (user, message) { var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); var encodedMsg = user + " says " + msg; var li = document.createElement("li"); li.textContent = encodedMsg; document.getElementById("messagesList").appendChild(li); }); //開啟連接. connection.start().catch(function (err) { return console.error(err.toString()); }); document.getElementById("sendButton").addEventListener("click", function (event) { var user = document.getElementById("userInput").value; var message = document.getElementById("messageInput").value; //調用服務器的 SendMessage 方法,並傳入兩個入參.這和委托的調用太像了. connection.invoke("SendMessage", user, message).catch(function (err) { return console.error(err.toString()); }); event.preventDefault(); });
上面的 ChatHub 類的 SendAsync 方法在調用客戶端的方法時,方法名是直接寫的 字符串 : "ReceiveMessage"
官方不推薦這樣寫,因為不是強類型,可能出現運行時錯誤,因此建議使用強類型的中心
(順帶附上了一些額外的功能):
//建議使用下面的強類型方式 //方法二 public interface IChatClient { //就算是這種強類型方式,客戶端定義的方法名也必須和這個方法名一樣,包括簽名. Task ReceiveMessage(string user, string message); } public class StronglyTypedChatHub : Hub<IChatClient> { //[HubMethodName("hello")] 可以改名,如果改了名,前端也要跟着改,別忘了. public async Task SendMessage(string user, string message) { //調用客戶端定義的 ReceiveMessage 方法. //throw new HubException("哈哈,出錯了!");//可以向客戶端發送異常.只會向當前調用的客戶端發送,並且只發送 message ,不會發送堆棧信息.
//Clients.Caller 當前客戶端
//Clients.Others 當前客戶端的其他客戶端 await Clients.All.ReceiveMessage($"{GetHashCode()}" + user, message);//傳遞 hashCode 是為了證明,每次調用都是不同的實例.所以官方說不要在"中心"里面存狀態. } //該方法可以在客戶端連接上后,執行操作 public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnConnectedAsync(); } //同理,當客戶端斷開連接時執行的操作 public override async Task OnDisconnectedAsync(Exception exception) { await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users"); await base.OnDisconnectedAsync(exception); } }
當然,注冊路由的代碼也得換了 : app.UseSignalR(routes => { routes.MapHub<StronglyTypedChatHub>("/chatHub"); });
好困,明天繼續
2019.1.9
復制一段官方的文檔.
SignalR 是什么?
ASP.NET Core SignalR 是一個開源代碼庫,它簡化了向應用添加實時 Web 功能的過程。 實時 Web 功能使服務器端代碼能夠即時將內容推送到客戶端。
SignalR 的適用對象:
- 需要來自服務器的高頻率更新的應用。 例如:游戲、社交網絡、投票、拍賣、地圖和 GPS 應用。
- 儀表板和監視應用。 示例包括公司儀表板、銷售狀態即時更新或行程警示。
- 協作應用。 協作應用的示例包括白板應用和團隊會議軟件。
- 需要通知的應用。 社交網絡、電子郵件、聊天、游戲、行程警示以及許多其他應用都使用通知。
SignalR 提供了一個用於創建服務器到客戶端遠程過程調用(RPC)的 API。 RPC 通過服務器端 .NET Core 代碼調用客戶端上的 JavaScript 函數。
以下是 ASP.NET Core SignalR 的一些功能:
- 自動管理連接。
- 同時向所有連接的客戶端發送消息。 例如,聊天室。
- 將消息發送到特定的客戶端或客戶端組。
- 擴展以處理增加的流量。
傳輸
SignalR 支持幾種方法用於處理實時通信:
- WebSockets
- 服務器發送事件
- 長輪詢
SignalR 會從服務器和客戶端支持的功能中自動選擇最佳傳輸方法
中心
SignalR 使用中心在客戶端和服務器之間進行通信。
“中心”是一種高級管道,允許客戶端和服務器相互調用方法。 SignalR 自動處理跨計算機邊界的調度,允許客戶端和服務器相互調用方法。 可以將強類型參數傳遞給方法,從而啟用模型綁定。 SignalR 提供兩個內置中心協議:基於 JSON 的文本協議和基於 MessagePack 的二進制協議。 與 JSON 相比,MessagePack 創建的消息通常比較小。 舊版瀏覽器必須支持 XHR 2 才能提供 MessagePack 協議支持。
中心通過發送包含客戶端方法的名稱和參數的消息來調用客戶端代碼。 使用配置的協議對作為方法參數發送的對象進行反序列化。 客戶端會嘗試將方法名稱與客戶端代碼中的方法匹配。 當客戶端找到匹配項時,它會調用該方法並將反序列化的參數數據傳遞給它。
發送中心外的信息
上面講的例子,客戶端和服務器的消息都是在"中心"里面流動.實際上,中心還可以把中心之外的信息發送給客戶端.(這句話可能有點繞),看示例就明白了.
新建一個測試控制器, 依然通過依賴注入得到 IHubContext 類的實例, StronglyTypedChatHub 類就是上面例子中的那個自定義的強類型中心.
[Route("api/[controller]")] [ApiController] public class TestController { private IHubContext<StronglyTypedChatHub, IChatClient> _context; public TestController(IHubContext<StronglyTypedChatHub, IChatClient> context) { _context = context; } public void Get() { _context.Clients.All.ReceiveMessage("admin", "在座的各位都是垃圾!"); } }
測試效果:
這個是強類型中心的例子,下面是普通中心的例子:
[Route("api/[controller]")] [ApiController] public class TestController { //強類型中心 //private IHubContext<StronglyTypedChatHub, IChatClient> _context; //public TestController(IHubContext<StronglyTypedChatHub, IChatClient> context) //{ // _context = context; //} //普通中心 private IHubContext<ChatHub> _context; public TestController(IHubContext<ChatHub> context) { _context = context; } public void Get() { //這種調用客戶端方法的方式,好別扭 _context.Clients.All.SendAsync("ReceiveMessage", "admin", "在座的各位都是垃圾!!!"); } }