文章索引

  1. 系列开篇
  2. SignalR 部分核心原理
  3. WebSocket部署问题和优化若干问题
  4. 身份验证和授权
  5. 服务端主动推送
  6. SignalR JavaScript 客户端
  7. AngularJS 应用
  8. 异常处理

想要了解SignalR得先了解WebSocket,因为大可直接说SignalR的实现是为了实现类似WebSocket的实时通信。

什么是WebSocket

它的目标是为了让浏览器和服务端双向通信,即我们可以按传统请求-接收或服务端主动发送数据给客户端。WebSocket他并非是一个功能型东西,是一种协议,得要明白这一点,但凡浏览器和服务器实现这种协议,就可以直接在任何浏览器、平台(Win/Linux等等)上实现WebSocket。

在WebSocket协议中规定,浏览器和服务器只需要一次握手,然后彼此之间就有了爱意、可以相互抛媚眼。因此这种规范带来两大优点:

  • Header 极少,2字节。不言而喻,极大减少宽带。
  • Server Push即服务端可以主动推送。

WebSocket 目前在浏览器上支持情况,可以参考测试页

另一种服务器推技术:Comet

如同WebSocket的Server Push一样。对于客户端而言有两种方式:

一是基于套接口,这需要运用像Flash XMLSocket或Java Applet套接口。

二是基于HTTP长连接,而实现这一技术有三种:AJAX长轮询、iframe、htmlfile流方式。

Comet是一种兼容性很好,像很多新浪微博、QQ邮箱、GTALK等等都使用该技术。可以参考这篇文章具体实现方案。

SignalR

应该说SignalR是上种两种的一个结合,甚至支持更多方案。SignalR提供四种数据传输方式,来解决浏览器兼容问题,他们默认分别按以下来判断该采用哪种方式:

  1. webSockets
  2. serverSentEvents
  3. longPolling
  4. foreverFrame 针对IE有效的一种变态做法,我没有细看。

以上四种具体实施方案百度非常多,很全,这里不再赘述。

一个完整示例

百度上已经有很多关于聊天室的示例,因为聊天室的确非常能说明SignalR的优点,而这里示例是设计一个关于消息提醒功能。

假定需求:用户登录后接收私信或系统异常报告通知及当前服务器时间。

1、首先我们可以通过NuGet很轻松安装SignalR,并在 Startup.cs 启用SignalR的支持。

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);

        app.MapSignalR(); // 启用SignalR
    }
}

MapSignalR 是默认的注册SignalR入口,这里我们完全可以自定义任意对SignalR具体实现接口。比如重新定义服务器地址(默认是 /signalr)、自定义身份证验证和授权机制等等。对于整个SignalR框架而言,他们都是采用依赖注入,换言之我们可以最大化扩展符合业务逻辑能力。

以下是将服务器地址变更为 /demo,且禁止生成生成客户端代理脚本。

app.MapSignalR("/demo", new Microsoft.AspNet.SignalR.HubConfiguration()
{
    EnableJavaScriptProxies = false
});

2、创建Hub类

为了演示需要,我分别创建 GetNow()GetCount() 两个方法:

GetNow():任何人都可以访问,对所有连接者发送当前时间。

GetCount():需要登录后才可以访问,发送自己的消息数据。

[HubName("msgHub")]
public class MessageHub : Hub
{
    public void GetNow()
    {
        Clients.All.pushNow(DateTime.Now);
    }

    [Authorize]
    public void GetCount()
    {
        List<MessageItem> ls = new List<MessageItem>();
        ls.Add(new MessageItem()
        {
            count = new Random().Next(1, 99),
            title = "异常数量",
            url = "/ex"
        });
        ls.Add(new MessageItem()
        {
            count = new Random().Next(1, 99),
            title = "私信",
            url = "/msg"
        });
        Clients.Caller.pushGet(ls);
    }
}

[Authorize] 默认采用的是ASP.NET的验证机制通道,换句话说当网站采用Windows登录验证,那么这里的 [Authorize] 也直接采用此方式。而对于SignalR自身已经有握手Key,来保证连接安全。

而往往默认的身份验证和授权机制很难符合业务需求,所以也可以直接继承 Microsoft.AspNet.SignalR.AuthorizeAttribute 自定义授权和验证规则,这一点后面会有专门文章来说明。

当采用 [Authorize] 属性后,就可以在方法里头调用 Context.User 对象访问当前用户信息。

另外非常重要的一点:app.MapSignalR(); 必须是 ConfigureAuth(app); 之前,因为我们都懂。

3、HTML代码

当开启 EnableJavaScriptProxies 时(默认就是开启),就可以享受SignalR自动生成的一个Hub客户端脚本代码。所以我需要先引入这些脚本;包括jQuery.js、jquery.signalR.js两个核心文件。

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.signalR-2.1.2.js"></script>
<script src="~/demo/hubs"></script>
<p id="J_Now"></p>
<p id="J_GetCount"></p>

其中 ~/demo/hubs 由SignalR自动生成,这里的 /demo 是指在前面变更的服务器地址。

4、启动Hub

虽然SignalR自动生成Hub客户端脚本,但此时还是需要添加一些 client 以便于服务器推送时接收数据,和启用Hub。

(function () {
    var msgHub = $.connection.msgHub;

    $.extend(msgHub.client, {
        pushNow: function (res) {
            $('#J_Now').html(res);
        },
        pushGet: function (res) {
            $('#J_GetCount').html(JSON.stringify(res));
        }
    });

    $.connection.hub.start().done(function () {
        init();
    }).fail(function () {
        // 连接失败处理
    });


    function init() {
        setInterval(function () { msgHub.server.getNow(); }, 1000); // 按理应该是由服务器直接推送,但我想留到另一节说,不要问为什么,任性。

        msgHub.server.getCount().fail(function (res) {
            console.log(res);
        });
    }
})();

每个Hub都有对应 clientserver,而SignalR会反射类下的所有公共方法,并把这些自动生成拼接成JavaScript放进 server 集合里头。

client:表示由服务端推送数据时,客户端接收对象,比如:Clients.Caller.pushGet(ls); 中的 pushGet

server:表示主动请求服务端的方法,比如:GetCount()

对于 server.getCount() 返回的是一个 promise() 对象,所以这里可以直接 done()fail() 来处理一些失败或成功时操作。

以上,就是一个完整的示例。

总结

SignalR 不光解决了我们多种实时通信的方案,而且兼容性非常好,这是一个相当好的项目,不久将来一定会有越来越多项目使用,甚至我们可以整个项目都采用 SignalR。这对于用户而言是极棒的体验。