實時web應用方案——SignalR(.net core)

何爲實時

先從理論上解釋一下二者的區別。前端

大多數傳統的web應用是這樣的:客戶端發起http請求到服務端,服務端返回對應的結果。像這樣:git

 

也就是說,傳統的web應用都是客戶端主動發起請求到服務端。web

那麼實時web應用呢?它不須要主動發起請求,服務端能夠主動推送信息到客戶端。後端

舉栗子的話,實時聊天工具、web遊戲等均可以算是實時應用。瀏覽器

什麼是SignalR

若是想作一個實時應用,最好用web socket。很早之前我也寫過web socket的實現方式,但不夠全面,這裏再補上一篇。服務器

來講說signalR,它是一款開源的實時框架,可使用三種方式實現通訊(long polling、server sent events、web socket)。它很好的整合了底層技術,讓咱們能夠不用關注底層技術實現而把精力聚焦在業務實現上。一個完整的signalR包括客戶端和服務端,服務端支持net core/net framework,還支持大部分客戶端,好比瀏覽器和桌面應用。app

回落機制

爲了兼容不一樣瀏覽器(客戶端)和服務端,signalR採用了回落機制,使得它能夠根據狀況協商使用不一樣的底層傳輸方式。假如瀏覽器不支持web socket,就自動降級使用sse,再不行就long polling。固然,也能夠禁用這種機制,指定其中一種。框架

三種通訊方式

long polling(長輪詢)

長輪詢是客戶端發起請求到服務端,服務器有數據就會直接返回。若是沒有數據就保持鏈接而且等待,一直到有新的數據返回。若是請求保持到一段時間仍然沒有返回,這時候就會超時,而後客戶端再次發起請求。socket

這種方式優勢就是簡單,缺點就是資源消耗太多,基本是不考慮的。async

server sent events(sse)

若是使用了sse,服務器就擁有了向客戶端推送的能力,這些信息和流信息差很少,期間會保持鏈接。

這種方式優勢仍是簡單,也支持自動重連,綜合來說比long polling好用。缺點也很明顯,不支持舊的瀏覽器不說,還只能發送本文信息,並且瀏覽器對sse還有鏈接數量的限制(6個)。

web socket

web socket容許客戶端和服務端同時向對方發送消息(也就是雙工通訊),並且不限制信息類型。雖然瀏覽器一樣有鏈接數量限制(多是50個),但比sse強得多。理論上最優先使用。

進入正題

開始以前,還須要瞭解RPC和Hub的概念。

RPC:全程Remote Procedure Call,字面意思遠程服務調用,能夠像調用本地方法同樣調用遠程服務。前端能夠調用後端方法,後端也能夠調用前端方法。

Hub:基於RPC,接受從客戶端發過來的消息,也同時負責把服務端的消息發送給客戶端。客戶端能夠調用Hub裏面的方法,服務端能夠經過Hub調用客戶端裏面的方法。

好了,概念已經理解清楚了,接下來上代碼。

在項目裏新增Hub類:

using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

namespace SignalRDemo.Server
{
    public class SignalRHub : Hub
    {
        /// <summary>
        /// 客戶鏈接成功時觸發
        /// </summary>
        /// <returns></returns>
        public override async Task OnConnectedAsync()
        {
            var cid = Context.ConnectionId;

            //根據id獲取指定客戶端
            var client = Clients.Client(cid);

            //向指定用戶發送消息
            await client.SendAsync("Self", cid);

            //像全部用戶發送消息
            await Clients.All.SendAsync("AddMsg", $"{cid}加入了聊天室");
        }
    }
}

爲了讓外部能夠訪問,咱們還須要一個控制器。在控制器裏聲明隨便建一個:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SignalRDemo.Server;
using System.Threading.Tasks;

namespace SignalRDemo.Controllers
{
    public class HomeController : Controller
    {
        private readonly IHubContext<SignalRHub> _countHub;

        public HomeController(IHubContext<SignalRHub> countHub)
        {
            _countHub = countHub;
        }

        /// <summary>
        /// 發送信息
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task Send(string msg, string id)
        {
            await _countHub.Clients.All.SendAsync("AddMsg", $"{id}:{msg}");
        }
    }
}

再而後進入StartUp設置端點:

endpoints.MapHub<SignalRHub>("/hub");

完成之後,配置signalr客戶端:

setupConn = () => {
    conn = new signalR.HubConnectionBuilder()
        .withUrl("/hub")
        .build();

    conn.on("AddMsg", (obj) => {
        $('#msgPanel').append(`<p>${obj}</p>`);
    });

    conn.on("Finished", () => {
        conn.stop();
        $('#msgPanel').text('log out!');
    });

    conn.on("Self", (obj) => {
        $('#userId').text(obj);
    });

    conn.start()
        .catch(err => console.log(err));
}

要注意withUrl裏面的路徑就是以前設置好的端點。

運行效果:

 

 

 Hub還支持組操做,好比:

//將用戶添加到A組
await
Groups.AddToGroupAsync(Context.ConnectionId, "GroupA");
//將用戶踢出A組
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "GroupA");
//向A組全部成員廣播消息
await Clients.Group("GroupA").SendAsync("AddMsg", "羣組消息");

更多操做請參考官方文檔。

本文演示demo的源碼見git,地址:https://gitee.com/muchengqingxin/SignalRDemo.git

相關文章
相關標籤/搜索