关于 SignalR 的说明
这是一篇关于 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:首次修订