天天看點

SignalR實作從服務端主動發送資料到用戶端

場景:服務端在背景開啟了一個HttpListener的監聽器來監聽第三方裝置通過http協定發送過來的裝置實時資料,當服務端接收到裝置的實時資料後,通過SignalR技術将資料發送到前端

我們先看服務端添加SignalR元件的步驟吧。

首先我們要從NuGet上查找SignalR元件,安裝以下兩個元件:

SignalR實作從服務端主動發送資料到用戶端

在目前項目上右擊,添加建立項,找到OWIN Startup類,點選添加。代碼如下:

public void Configuration(IAppBuilder app)
        {
            // 有關如何配置應用程式的詳細資訊,請通路 https://go.microsoft.com/fwlink/?LinkID=316888
            app.MapSignalR();
        }
           

然後添加建立項,SignalR Hub Class,即添加SignalR的集線器類。集線器類的代碼如下:

[HubName("HubDemo")]
    public class HubDemo : Hub
    {
        private readonly HubDemoService _hubDemoService;      
        public static HubDemo Instance { get; } = new HubDemo(HubDemoService.Instance);

        public HubDemo() : this(HubDemoService.Instance)
        {
            
        }

        public HubDemo(HubDemoService hubDemoService)
        {        
            _hubDemoService = hubDemoService;
        }

        public void Hello(string projectID)
        {           
            Groups.Add(Context.ConnectionId, projectID);
            _hubDemoService.Hello();       
        }

        public void SendClient(string deviceSN)
        {       
            _hubDemoService.SendClient(deviceSN);
        }
    }
           

這裡我們建了一個集線器類的服務類來完成具體的資料發送任務,服務類的代碼如下:

public class HubDemoService
    {
        private HubDemoService(IHubConnectionContext<dynamic> clients)
        {
            Clients = clients;           
        }

        private IHubConnectionContext<dynamic> Clients
        {
            get;
            set;
        }

        public static HubDemoService Instance { get; } = new HubDemoService(GlobalHost.ConnectionManager.GetHubContext<HubDemo>().Clients);

        public void Hello()
        {
            Clients.All.Hello(true);
        }
        public void SendClient(string deviceSN)
        {
            Clients.Group(deviceSN).SendClient("裝置資料");
        }
    }
           

集線器類和服務類我們都使用的是單例模式。而且我們使用了分組方法,分組的概念就相當于微信群,你發出去的消息隻有這個群的人才能看到,其他人是收不到這個你發的消息的。

下面我們看看怎麼調用集線器類的SendClient方法來主動把資料發送到用戶端。我在Home控制器中添加了一個視圖,這就是一個很簡單的視圖,SignalR的用戶端操作就寫在這個視圖裡面,代碼如下:

public ActionResult SendClient()
        {            
            return View();
        }

        public string AjaxPost()
        {
            HubDemo.Instance.SendClient("10000");
            return "ajax傳回";
        }
           
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>SendClient</title>
    
</head>
<body>
    <div>
        <button id="btn1" onclick="btnclick()">發送請求</button>
    </div>
    <script src="~/Scripts/jquery-3.4.1.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.2.js"></script>
    <script src="~/signalr/hubs"></script>
    <script type="text/javascript">
        var hubDemo = $.connection.HubDemo;
        hubDemo.client.Hello = function (success) {
            if (success) {
                console.log("初始化連接配接成功!");
            }
            else {
                console.log("初始化連接配接失敗!");
            }
        }
        
        hubDemo.client.SendClient = function (data) {

            console.log(data);
        }     
        $.connection.hub.start().done(function () {
            hubDemo.server.hello("10000");
        });
        
        function btnclick() {        
            $.ajax({
                url: "/Home/AjaxPost",
                type:"GET",
                success: function (data) {
                    
                }
            });
        }
    </script>
</body>
</html>
           

要想使用SignalR,必須添加js代碼中的3個js,其中/signalr/hubs是SignalR自動生成的。

在start完成後,我們調用了伺服器的hello方法來把目前裝置序列号發送給服務端,服務端會用這個裝置序列号生成一個分組。當服務端收到這個裝置序列号的資料後,就隻把裝置的資料發送給目前這個用戶端,避免了沒有監聽目前裝置的用戶端也收到這個裝置的資料。

這裡有個地方一定要注意,就是hello這個方法的大小寫問題,雖然服務端的Hello方法H是大寫的,但是在用戶端調用的時候,H必須是小寫,不然用戶端會提示你Hello不是一個方法,如圖:

SignalR實作從服務端主動發送資料到用戶端

還有一個問題也要注意,就是我們不能在服務端傳回View()之前發送資料到用戶端,如:

public ActionResult SendClient()
        {
            HubDemo.Instance.SendClient("10000");
            return View();
        }
           

因為服務端傳回View()之後才會對html頁面進行加載,在傳回View()之前調用SendClient,對應的視圖裡面的js代碼都還沒有執行,是以用戶端是收不到資料的。

為了解決這個問題,我在界面上加了一個按鈕,通過點選這個按鈕,發送一個ajax的請求,服務端接收到請求後,再發送裝置資料發送到用戶端。通過按鈕觸發ajax請求這個步驟其實是模拟服務端接收到裝置發送過來的資料。

啟動程式,進入到SendClient,頁面加載完成後,點選發送請求按鈕

用戶端輸出如下:

SignalR實作從服務端主動發送資料到用戶端