天天看点

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实现从服务端主动发送数据到客户端