65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 SignalR 的说明

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (17投票s)

2016 年 10 月 27 日

CPOL

5分钟阅读

viewsIcon

30201

downloadIcon

330

这是一篇关于 SignalR 的简短笔记。

引言

这是一篇关于 SignalR 的简短笔记。

背景

SignalR 是微软实现的 WebSocket,它通过单个 TCP 连接提供 全双工通信通道。但这篇笔记只讨论了从服务器向浏览器发送信号。除了提供一个可运行的示例外,这篇笔记的重点是创建一个自定义客户端代理,以解决微软自动生成代理存在的以下限制:

  • 如果连接中断,自动生成的代理不会尽力重新建立连接。
  • 自动生成的代理不支持在连接建立后添加客户端监听器。

附带的是一个在 Visual Studio 2015(更新 3)中创建的典型 ASP.NET MVC 应用程序。虽然它只讨论了向所有连接的浏览器广播信号的主题,但如果您想在微软环境中更深入地探索 WebSocket 的复杂主题,它应该是一个简单的起点。

服务器端环境

要在 ASP.NET MVC 项目中使用 SignalR,您需要在 NuGet 包中添加 SignalR 依赖项。

您需要创建一个“Hub”类,以便 Web 浏览器可以通过 WebSocket 连接到服务器。

using Microsoft.AspNet.SignalR;
    
namespace siganlR_example
{
    public class SignalRHub: Hub { }
}
  • 您可以随意命名“Hub”类。在此示例中,我将其命名为“SignalRHub”,但您不必将其命名为相同。此类需要继承自 NuGet 包随附的“Microsoft.AspNet.SignalR.Hub”类;
  • 为了本笔记的目的,我只向所有连接的浏览器广播信号,因此我将此类留空。如果您想对 WebSocket 做更多事情,您可以阅读 SignalR 文档,并根据您的意愿向此类添加任何内容。

为了让 MVC 应用程序能够识别“SignalRHub”类,您需要在应用程序启动时调用“IAppBuilder”接口上的扩展方法“MapSignalR()”。

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // Map the SignalR
        app.MapSignalR();
    
        AreaRegistration.RegisterAllAreas();
        RouteConfig.RegisterRoutes(RouteTable.Routes);
    }
}

MapSignalR()”函数会在应用程序启动时搜索“Microsoft.AspNet.SignalR.Hub”类的所有子类,以便 Web 应用程序知道当浏览器尝试连接它们时应该使用哪些“Hub”类。

客户端环境

要使用 SignalR,您将需要客户端 SignalR JavaScript 库。

<script src="@Url.Content("~/Scripts/jquery.min-3.1.0.js")"></script>
<script src="@Url.Content("~/Scripts/jquery.signalR-2.2.1.min.js")"></script>
  • SignalR 依赖于 jQuery,因此我们需要 jQuery 库;
  • 您可以通过 Nuget 或从 Microsoft CDN 获取 SignalR JavaScript 库。我个人偏好从 Nuget 获取 JavaScript 库,所以我只是从 CDN 下载了“jquery.signalR-2.2.1.min.js”文件。

微软提供了一个 自动生成的客户端代理,但它存在以下缺点:

  • 如果连接丢失,它不会尽力重新连接。
  • 如果连接建立后才添加监听器,它将无法接收信号。

为了解决这两个缺点,我在“SignalRClient.js”文件中创建了自己的客户端代理。

var SignalRClient = function (url, hubName) {
    var self = this;
    var RECONNECT_IF_DISCONNECTED = true;
    
    var connectionUrl = url;
    var connection = $.hubConnection();
    var proxy = connection.createHubProxy(hubName);
    
    // Register a dummy handler
    proxy.on('*', function () { });
    
    var connectToServer = function () {
    
        connection.url = connectionUrl;
        connection.start()
        .done(function () {
            console.log('SignalR Connected ID=' + connection.id);
        })
        .fail(function () {
            console.log('SignalR Connection failed');
        });
    };
    
    connection.disconnected(function () {
        console.log('SignalR Disconnected');
    
        // If 'RECONNECT_IF_DISCONNECTED' set the timer
        // to connect to the server until re-connected
        if (RECONNECT_IF_DISCONNECTED) {
            setTimeout(connectToServer, 5 * 1000);
        }
    });
    
    self.on = function (caller, handler) {
        proxy.on(caller, handler);
        return self;
    };
    
    self.connect = function () {
        RECONNECT_IF_DISCONNECTED = true;
        connectToServer();
    };
    
    self.disconnect = function () {
        RECONNECT_IF_DISCONNECTED = false;
        connection.stop();
    };
};
  • SignalRClient”是一个典型的 JavaScript 构造函数。
  • 起初,它会注册一个虚拟的监听器,这允许在连接建立后添加监听器。您可以在此链接查看详细信息。
  • 在“disconnected”事件中,我添加了代码来尽力重新连接到服务器,直到连接重新建立。您可以在此链接查看详细信息。您应该知道,“disconnected”事件会在连接断开时触发,并且在调用“connect”方法进行初始连接尝试失败时也会触发。

示例

从服务器向连接的浏览器广播消息非常简单。

protected void Application_Start() {
    
    Timer timer = new Timer(1000);
    timer.AutoReset = true;
    
    timer.Elapsed += (sender, e) =>
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("Server Time: ")
            .Append("<span style=\"color: red\">")
            .Append(System.DateTime.Now.ToLongTimeString())
            .Append("</span>");
    
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
        context.Clients.All.JustSendAMessage(sb.ToString());
    };
    
    timer.Start();
}
  • 在本例中,我在“Application_Start”事件中设置了一个计时器,每秒一次将服务器时间发送到浏览器;
  • 重要的是要知道“JustSendAMessage()”是对动态类型的调用。即使它只是一个 .NET 语法糖,“JustSendAMessage”这个文本很重要。当我们在客户端注册监听器时,我们需要这个文本来接收消息。

为了接收来自服务器的消息,我创建了“Index.cshtml”。

<html>
<body>
    <div id="spanMsg"></div>
</body>
</html>

并将客户端 JavaScript 文件链接添加到 HTML 文档中。

<script src="@Url.Content
("~/Scripts/jquery.min-3.1.0.js")"></script>
<script src="@Url.Content
("~/Scripts/jquery.signalR-2.2.1.min.js")"></script>
<script src="@Url.Content
("~/Scripts/SignalRClient.js")"></script>
$(document).ready(function () {
    var sClient = new SignalRClient
    ('@Url.Content("~/signalR")', 'signalRHub');
    
        sClient.on('JustSendAMessage', function (msg) {
            $('#spanMsg').html(msg);
        });
        sClient.connect();
});
  • 要创建“SignalRClient”的实例,您需要提供“url”和“Hub”类的名称
    • url”始终是网站的根目录 +“/signalR”;
    • Hub”名称对应于服务器中 C#“SignalRHub”类的名称。您可能已经注意到,您需要将“S”的大小写更改为“s”。我不知道微软为什么必须让“Hub”名称的大小写与 C# 类不同,但它就是这样。
  • on”方法向由“JustSendAMessage” 动态类型发送的消息注册一个监听器。当收到新消息时,“spanMsg”将被更新以显示更新的服务器时间。

指定传输

SignalR 客户端与服务器之间的通信可以使用不同的传输。

  • WebSockets
  • ForeverFrame
  • ServerSentEvents
  • LongPolling

SignalR 将与服务器进行协商以找到最合适的传输,但有时我们希望能够选择要使用的传输。

connection.start({ transport: ['serverSentEvents', 'foreverFrame', 'webSockets'] })

上面的“start()”方法在客户端尝试连接服务器时排除了“longPolling”选项。

运行应用程序

如果您拥有相应版本的 Visual Studio,则可以加载项目并运行它。

  • 如果一切运行正常,您应该会看到通过 WebSocket 从服务器发送的消息。
  • 如果您有时间,可以尝试重新启动服务器,看看 SignalR 连接在中断后是否会重新建立。
  • 如果您有兴趣,也可以更改代码在 SignalR 连接建立后注册监听器。您应该会看到监听器能够很好地接收服务器消息。

关注点

  • 这是一篇关于 SignalR 的简短笔记。
  • 本笔记附带了一个非常简单的 SignalR 使用示例,它非常简单,只向所有连接的浏览器广播消息。
  • 希望您喜欢我的博文,并希望这篇笔记能以某种方式帮助您。

历史

  • 2016/10/25:首次修订
© . All rights reserved.