一、SignalR 概述
https://docs.microsoft.com/en-us/aspnet/signalr/index
SignalR是微软为实现实时通信的一个类库。一般情况下,signalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。

    signalR内部有两类对象:
  1. 持久连接
    一个持久连接代表了一个端点,它可以发送单一接收者,Group接受者或者广播信息。持久连接的api是SignalR提供给开发者进入低级别协议的api。连接模型使用起来和WCF比较类似。
  2. Hubs(集线器)
    Hubs是SignalR提供的高级别的api,它允许客户端和服务端,在自己这边相互调用对方的方法。Hubs模型类似于.Net Remoting。使用Hubs也可以让你传递强类型参数,进行模型绑定。
SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。



SignalR 和 WebSocket
如果客户端和服务器都支持WebSocket,那么SignalR会通过WebSocket来传输数据。当然你也可以自己使用WebSocket来实现SignalR的功能,不过使用SignalR你就不用考虑如果客户端或者服务器不支持WebSocket的问题了。

二、SignalR的协议选择
SignalR是可以在客户端和服务器端进行即时通讯的几种协议的抽象和实现。一个SignalR连接是通过http请求发起的,然后上升为WebSocket(如果客户端和服务端都支持)。WebSocket是SignalR最理想的协议,它可以有效地利用服务器端的内存,有着最低的延迟,最多的基础特性(比如客户端和服务端的全双工连接),不过它也有着严格的要求,需要服务器端使用Windows Server 2012或者Windows 8以上的系统,也需要.NET Framework 4.5.。如果不符合这些要求,那么SignalR会使用其他的协议来建立连接。

HTML 5协议
・WebSocket。如果服务器和客户端都支持,那么就使用WebSocket协议来进行通讯。
・服务器推送事件(Server-sent Events)。除了IE,其他的浏览器基本都支持。
Comet协议
・Forever Frame (只支持IE)。
・Ajax长轮询(Ajax long polling)。
SignalR协议选择过程
1.如果浏览器是IE8或者更早的版本,使用长轮询。
2.如果配置了Jsonp(如果连接开始的时候jsonp的参数设置为true), 使用长轮询。
3.如果是跨域连接, 如果下面的条件符合就会使用WebSocket,如果有条件不符合,那就是用长轮询。
    ・客户端支持跨域连接
    ・客户端支持WebSocket
    ・服务器端支持WebSocket
4.如果没有配置jsonp,而且不是跨域连接,如果客户端和服务端都支持WebSocket,那么就使用WebSocket。
5.如果客户端或者服务端不支持WebSocket,使用服务器推送事件。
6.如果不支持服务器推送事件,使用Forever Frame。
7.如果不支持Forever Frame,使用长轮询。

监控协议
可以通过在你的Hub上开启logging来监控你的SignalR使用了什么协议。
$.connection.hub.logging = true;

指定协议
SignalR判断协议也需要消耗一定的客户端、服务端资源,如果你清楚客户端、服务端支持的协议,那么你可以指定使用某种协议来建立连接。
比如,你知道客户端只支持长轮询,那么你可以指定使用长轮询来进行通讯。
connection.start({ transport: 'longPolling' });

你也可以指定一个序列,客户端会按照序列里的顺序来进行通讯。下面的代码的作用是,先使用WebSocket,如果失败了,就使用长轮询。
connection.start({ transport: ['webSockets','longPolling'] });

SignalR包含下面四种指定的协议常量
    ・webSockets
    ・foreverFrame
    ・serverSentEvents
    ・longPolling
三、SignalR的三种实现方式
1. 集线器类(Hub) + 非自动生成代理模式
服务端与客户端分别定义的相对应的方法,客户端通过代理对象调用服务端的方法,服务端通过IHubConnectionContext回调客户端的方法,客户端通过回调方法接收结果。
JS端调用服务端方法采用:chat.invoke,而被服务端回调的方法则采用:chat.on (这里的chat是createHubProxy创建得来的)
            var conn = $.hubConnection();
            conn.qs = { "clientName": clientName };
            conn.start().done(function () {
                $("#btnSend").click(function () {
                    var toUserId = eUsers.val();
                    if (toUserId != "") {
                        chat.invoke("sendOne", toUserId, $("#message").val())
                        .done(function () {
                            //alert("发送成功!");
                            $("#message").val("").focus();
                        })
                        .fail(function (e) {
                            alert(e);
                            $("#message").focus();
                        });
                    }
                    else {
                        chat.invoke("send", $("#message").val())
                        .done(function () {
                            //alert("发送成功!");
                            $("#message").val("").focus();
                        })
                        .fail(function (e) {
                            alert(e);
                            $("#message").focus();
                        });
                    }
                });
            });
 
            var chat = conn.createHubProxy("chat");
            chat.on("receiveMessage", function (dt, cn, msg) {
                var clsName = "linfo";
                if (cn == clientName || cn.indexOf("您对") >= 0) clsName = "rinfo";
                eChatBox.append("<p class='" + clsName + "'>" + dt + " <strong>" + cn + "</strong> 说:<br/>" + msg + "</p>");
                eChatBox.scrollTop(eChatBox[0].scrollHeight);
            });
 
            chat.on("userChange", function (dt, msg, users) {
                eChatBox.append("<p>" + dt + " " + msg + "</p>");
                eUsers.find("option[value!='']").remove();
                for (var i = 0; i < users.length; i++) {
                    if (users[i].Value == clientName) continue;
                    eUsers.append("<option value='" + users[i].Key + "'>" + users[i].Value + "</option>")
                }
            });

2. 集线器类(Hub)+ 自动生成代理模式
需要js引用
<script src="~/signalr/hubs" type="text/javascript"></script>
然而,我们在写代码的时候上面的引用并不存在,而当运行后就会自动生成上述signalr的代理脚本
这就是与非自动生成代理脚本最根本的区别,也正是因为这个自动生成的脚本,我们可以在JS中更加方便的调用服务端方法及定义回调方法,调用服务端方法采用:chat.server.XXX,而被服务端回调的客户端方法则采用:chat.client.XXX

3.持久化连接类(PersistentConnection)
Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection")
需实现继承自PersistentConnection类的自定义的持久化连接类,在这个连接中可以重写:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同时有几个重要的属性成员Connection、Groups,服务端发消息给客户端采用:Connection.Broadcast(广播,所有客户端都可以收到消息),Connection.Send(发送给指定的客户端)

具体实现参考
https://www.cnblogs.com/zuowj/p/5674615.html

四、使用RignalR实现新消息推送(集线器类(Hub)+ 自动生成代理模式
1.app.MapSignalR();
using System.Data.Entity;
using Microsoft.Owin;
using Owin;
using RCRS.WebApp.Town.Migrations;
using RCRS.WebApp.Town.Models.DomainEntity;

[assembly: OwinStartupAttribute(typeof(RCRS.WebApp.Town.Startup))]
namespace RCRS.WebApp.Town
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
            app.MapSignalR();

            Database.SetInitializer(new MigrateDatabaseToLatestVersion<TownContext, TownConfiguration>());
        }
    }
}

2. NotificationHub
using System.Linq;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using RCRS.WebApp.Town.Models.Town;

namespace RCRS.WebApp.Town.Hubs
{
    [HubName("NotificationHub")] 
    public class NotificationHub : Hub
    {
        public void Connect(string userId)
        {
            var id = Context.ConnectionId;

            if (BizHub.ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
            {
                BizHub.ConnectedUsers.Add(new HubUser { ConnectionId = id, UserId = userId });
                // send to caller
                Clients.Caller.onConnected(id, userId, BizHub.ConnectedUsers);

                // send to all except caller client
                Clients.AllExcept(id).onNewUserConnected(id, userId);
            }
        }

        public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
        {
            var item = BizHub.ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
            if (item != null)
            {
                BizHub.ConnectedUsers.Remove(item);

                var id = Context.ConnectionId;
                Clients.All.onUserDisconnected(id, item.UserId);

            }
            return base.OnDisconnected(stopCalled);
        }

    }
}

3.BizHub
        /// <summary>  </summary>
        public static List<HubUser> ConnectedUsers = new List<HubUser>();

        public void NotifyAll(string msg)
        {
            var hub = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
            hub.Clients.All.broadcaastNotif(msg);
        }

        public void NotifyPrivate(string toUserId, string msg)
        {
            var toUser = ConnectedUsers.FirstOrDefault(x => x.UserId == toUserId);
            var hub    = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();

            if (toUser != null)
            {
                // send to 
                hub.Clients.Client(toUser.ConnectionId).broadcaastNotif(msg);
            }
        }

        public void NotifyRole(List<string> roleLs, string msg)
        {
            List<string> lsUserIds = new List<string>();

            using (ApplicationDbContext context = new ApplicationDbContext())
            {
                string cmd = getUsersByRoleLs(roleLs);
                lsUserIds = context.Database.SqlQuery<string>(cmd).ToListAsync().Result;
            }

            foreach (string toUserId in lsUserIds)
                NotifyPrivate(toUserId, msg);
        }

4.引用js
bundles.Add(new ScriptBundle("~/bundles/signalR").Include(
                "~/Scripts/jquery.signalR-2.2.3.js"));

5.
<script src="~/signalr/hubs"></script>
    <script type="text/javascript">
        $(function () {
            var id = '@ViewBag.UserId';
            var notifyHub = $.connection.NotificationHub;
            notifyHub.client.broadcaastNotif = function (message) {
                $("#assist-top-new-msg").text(message);
                $("#assist-msg-list-new-flg").text(message);
            };

            $.connection.hub.start()
                .done(function () {
                    console.log("Notification Hub Connected!");
                    //Server Call
                    notifyHub.server.connect(id);
                })
                .fail(function () {
                    console.log("Could not Connect Notification Hub!");
                });

        });
    </script>



posted on 2018-05-23 15:02 Ying-er 阅读(1373) 评论(0)  编辑  收藏 所属分类: .Net

只有注册用户登录后才能发表评论。


网站导航: