ASP.NET SignalR 基础知识循序渐进(第一部分)
识别并学习使用 SignalR 基础
引言
在本篇文章中,我将介绍我最近学习到的 ASP.NET SignalR 的非常基础的知识,并通过一个最简单的示例(SignalREcho_no_packages.zip)来逐步演示,该示例使用了 PersistentConnection
。请注意,我已从解决方案中删除了 packages 文件夹,因为上传大小太大。您可以通过 NuGet 在本地获取 Microsoft.AspNet.SignalR 进行构建。好的,让我们从一些基本问题开始。
ASP.NET SignalR 可以做什么?
- 服务器通过 HTTP 连接将数据推送到客户端
- 服务器到客户端的 RPC(远程过程调用)在 HTTP 连接中
- 通过 SQL Server、Redis 或 Service Bus 轻松扩展
SignalR 使用什么技术来传输数据?
- 如果可用,则使用 WebSockets
- 否则,使用其他技术,例如长轮询
SignalR 支持传输什么类型的数据?
- 未压缩的 JSON 文本或纯文本
- 如果您想传输压缩的 JSON、BSON 或二进制数据,则需要对数据进行编码和解码(例如使用 Base64),或者在服务器端实现自定义逻辑的
IJsonSerializer
,并在客户端实现相同的逻辑。请注意,SignalR 内部使用了TextReader
和TextWriter
。这意味着您无论如何都必须在传输之前将数据转换为文本。
当前稳定版本是 1.1.3。2.0 版本处于 Beta 阶段。许可证是 Apache 2.0。官方网站是 http://signalr.net。文档可以在 这里 找到。
软件包
所有 SignalR 开发包都可以通过 NuGet 获取。通常,您只需要获取 Microsoft.AspNet.SignalR
(包 ID),它包含了您需要的大部分组件,包括:
Microsoft.AspNet.SignalR.Core
:用于构建 SignalR 端点的服务器端组件。Microsoft.AspNet.SignalR.Owin
:SignalR 的 OWIN 宿主。Microsoft.AspNet.SignalR.SystemWeb
:用于在 ASP.NET 中宿主 SignalR(通过 OWIN ASP.NET 宿主)。Microsoft.AspNet.SignalR.Js
:SignalR 的 JavaScript 客户端。- 依赖项也包含在内:jQuery、
Newtonsoft.Json
、Microsoft.Owin.Host.SystemWeb
、Microsoft.Web.Infrastructure
和 OWIN。
您可以根据需要获取其他 SignalR 包:
Microsoft.AspNet.SignalR.Client
:SignalR 的 .NET 客户端(包括 WinRT、WP8 和 Silverlight5)。Microsoft.AspNet.SignalR.Redis
:用于在 Web 场中扩展 SignalR 的 Redis 消息传递后备。Microsoft.AspNet.SignalR.ServiceBus
:用于在 Web 场中扩展 SignalR 的 Windows Azure Service Bus 消息传递后备。Microsoft.AspNet.SignalR.SqlServer
:用于在 Web 场中扩展 SignalR 的 SQL Server 消息传递后备。Microsoft.AspNet.SignalR.Sample
:用于在您的应用程序中快速设置一个可运行的示例。
PersistentConnection
PersistentConnection
是用于在服务器端宿主 SignalR 服务的核心类。我将演示如何使用它。
首先,我创建一个新的 ASP.NET Web 应用程序,并使用 NuGet 获取 Microsoft.AspNet.SignalR
包。然后,我定义一个继承自 PersistentConnection
的新类。
public class MyConnection : PersistentConnection
{
protected override Task OnConnected(IRequest request, string connectionId)
{
string msg = string.Format(
"A new user {0} has just joined. (ID: {1})",
request.QueryString["name"], connectionId);
return Connection.Broadcast(msg);
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
// Broadcast data to all clients
string msg = string.Format(
"{0}: {1}", request.QueryString["name"], data);
return Connection.Broadcast(msg);
}
}
当客户端连接到我的服务时,会调用 OnConnected
。连接通过连接 ID(GUID)进行区分。客户端可以在建立连接时通过 IRequest.QueryString
传递一些信息。我们还可以从 IRequest
获取其他 HTTP 项,如 cookie、提交的表单、标头和用户主体。请注意,所有这些项都属于请求建立连接的第一个请求。
当客户端数据到达时,会调用 OnReceived
。connectionId
指示数据来自哪个连接。数据以文本格式传输。如果数据不代表纯文本,您需要自行解析。
PersistentConnection.Connection
提供了 Broadcast
方法将消息发送给所有已连接的客户端;它还提供了 Send
方法将消息发送给特定连接或组(使用组名作为 connectionId
)。两种方法都支持一个排除列表,用于指定不希望发送消息到的连接。
我们可以在 Global.asax 中使用 MyConnection
将我们的服务注册到路由表中。
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapConnection<MyConnection>("echo", "/echo");
}
}
现在我们可以编写网页中的 JavaScript 来连接到我们的服务。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SignalR Echo</title>
<script type="text/javascript" src="Scripts/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="Scripts/jquery.signalR-1.1.3.min.js"></script>
</head>
<body>
<script type="text/javascript">
function htmlEncode(value) {
return $("<div/>").text(value).html();
}
function addMsg(msg) {
$("#messages").append("<li>" + htmlEncode(msg) + "</li>");
}
$(function () {
$("#join").click(function () {
var connection = $.connection("/echo", "name=" + $("#name").val(), true);;
connection.received(function (data) {
addMsg(data);
});
connection.error(function (err) {
addMsg("Error: " + err);
});
addMsg("Connecting...");
connection.start(function () {
addMsg("Connected.");
$("#send").click(function () {
connection.send($("#msg").val());
});
});
});
});
</script>
<table>
<tr>
<td><span>Name:</span></td>
<td><input type="text" id="name" /></td>
<td><input type="button" value="Join" id="join" /></td>
</tr>
<tr>
<td><span>Message:</span></td>
<td><input type="text" id="msg" /></td>
<td><input type="button" value="Send" id="send" /></td>
</tr>
</table>
<ul id="messages"></ul>
</body>
</html>
接下来,让我们创建一个 .NET 客户端。只需创建一个新的控制台应用程序,并使用 NuGet 获取 Microsoft.AspNet.SignalR.Client
包。在 Main
函数中,我使用 Connection 类与我们的服务进行通信。
class Program
{
private const string ServiceUri = "https://:12722/echo";
static void Main(string[] args)
{
var connection = new Connection(ServiceUri, "name=dzy");
connection.Received += connection_Received;
connection.StateChanged += connection_StateChanged;
Console.WriteLine("Connecting...");
connection.Start().Wait();
string inputMsg;
while (!string.IsNullOrEmpty(inputMsg = Console.ReadLine()))
{
connection.Send(inputMsg).Wait();
}
connection.Stop();
}
static void connection_StateChanged(StateChange state)
{
if (state.NewState == ConnectionState.Connected)
{
Console.WriteLine("Connected.");
}
}
static void connection_Received(string data)
{
Console.WriteLine(data);
}
}
要查看服务是在 WebSockets 还是长轮询模式下运行,我们可以在 Windows 8 或 Windows Server 2012 上的 IIS 7.5 和 IIS 8.0 上宿主该服务。请注意,.NET WebSockets 仅在 Windows 8、Windows Server 2012 及以上版本上运行。然后,我们可以使用 Fiddler 检查客户端机器上的 HTTP 请求。
如果服务在 IIS 7.5 上宿主,连接请求会保持活动状态。

如果服务在 Windows 8 或 Windows Server 2012 的 IIS 8 上宿主,连接请求会要求升级到 WebSocket 连接。

摘要
在下一部分,我将介绍 SignalR Hubs、横向扩展和可扩展性。