十六、实时通信

在本章中,我们将了解 MicrosoftSignalR,这是一个用于在客户端和服务器之间进行实时通信的库。它允许服务器主动调用客户机,而不是作为请求的结果。它以众所周知的技术为基础,例如AJAXWebSocket、服务器发送事件,但采用透明的方式。你不需要知道它在使用什么,它基本上就是工作的,不管你有什么浏览器。它还支持很多浏览器,包括手机。让我们探索一下这项技术,看看它能提供什么,基本上如下所示:

  • 设置信号机
  • 将消息从客户端发送到服务器
  • 从服务器向所有/某些客户端广播消息
  • 从集线器外部发送消息

阅读本章后,您将学习如何从服务器实时通信到连接到页面的客户端,无论客户端是在 PC 上还是在移动设备上。

技术要求

要实现本章中介绍的示例,您需要.NET Core 3 SDK 和文本编辑器。当然,VisualStudio2019(任何版本)满足所有要求,但您也可以使用 VisualStudio 代码。

源代码可以在这里从 GitHub 检索:https://github.com/PacktPublishing/Modern-Web-Development-with-ASP.NET-Core-3-Second-Edition

设置信号机

在开始使用 signar 之前,首先需要解决几个问题,即在本地安装库。

执行以下步骤以开始此设置:

  1. 首先,安装Microsoft.AspNetCore.SignalRNuGet 包。
  2. 您还需要通过npm(简称节点包管理器)作为@microsoft/signalr提供的 JavaScript 库。
  3. 安装后,需要将 JavaScript 文件(最小化版本或调试版本)复制到wwwroot下的某个文件夹中,因为浏览器需要检索该文件。
  4. 包含信号器库的文件称为signalr.jssignalr.min.js(对于最小化版本),可在node_modules/@aspnet/signalr/dist/browser下找到。
  5. 如果您希望使用MessagePack序列化,您还需要@aspnet/signalr-protocol-msgpack包(稍后将对此进行详细介绍),但这不是严格需要的。

与以前的预核心版本不同,signar 不需要任何其他库,例如jQuery,但它可以愉快地与之共存。只需在使用代码之前添加对signalr.js文件的引用。

对于 npm,添加一个与此类似的package.json文件:

{
  "name": "chapter16",
  "version": "1.0.0",
  "description": "",
  "main": "",
  "scripts": {
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@microsoft/signalr": "^3.1.4",
    "@aspnet/signalr-protocol-msgpack": "^1.1.0",
    "msgpack5": "^4.2.1"
  }
}

npm 文件存储在node_modules文件夹中,但需要复制到wwwroot中的某个位置,从那里可以公开这些文件,例如,提供给浏览器。将文件从node_modules文件夹复制到应用中的一个好方法是利用MSBuild构建系统。打开您的.csproj文件并添加以下行:

<ItemGroup>
    <SignalRFiles Include="node_modules/@microsoft/signalr
    /dist/browser/*.js" />
    <SignalRMessagePackFiles 
        Include="node_modules/@aspnet/signalr-protocol-msgpack
        /dist/browser/*.js" />
    <MessagePackFiles Include="node_modules/msgpack5/dist/*.js" />
</ItemGroup>

<Target Name="CopyFiles" AfterTargets="Build">
    <Copy SourceFiles="@(SignalRFiles)"  
        DestinationFolder="$(MSBuildProjectDirectory)\wwwroot\lib
        \signalr" />
    <Copy SourceFiles="@(SignalRMessagePackFiles)" 
        DestinationFolder="$(MSBuildProjectDirectory)\wwwroot\lib
        \signalr" />
    <Copy SourceFiles="@(MessagePackFiles)" 
        DestinationFolder="$(MSBuildProjectDirectory)\wwwroot\lib\msgpack5" />
</Target>

这将把所需的 JavaScript 文件从 npm 提供的目录复制到wwwroot内的文件夹中,该文件夹适合包含在网页上。另一种选择是使用 Libman,如第 14 章客户端开发中所述。一定要看一看!不要忘记,因为您是在服务静态文件,所以必须在Configure方法中添加适当的中间件:

app.UseStaticFiles();

我们将从信号机的核心概念开始,并从那里开始。

学习核心概念

signar 的吸引力来自于它隐藏了处理(近)实时网络通信的不同技术。这些措施如下:

每个都有自己的优点和缺点,但对于 Signaler,您不需要真正关心这一点,因为 Signaler 会自动为您选择最好的。

那么,这是关于什么的?基本上,使用 Signal,您可以做两件事:

  • 让客户端应用(如网页)向服务器发送消息,并将其路由到也连接到同一 web 应用的所有(或某些)方
  • 让服务器主动向所有(或部分)连接方发送消息

消息可以是纯字符串或具有某种结构。我们不需要关心它;信号员为我们负责序列化。当服务器向连接的客户机发送消息时,它会引发一个事件,并让他们有机会对收到的消息进行处理。

SignalR 可以将连接的客户端分组,并要求进行身份验证。核心概念是集线器:客户端聚集在集线器周围,从集线器发送和接收消息。集线器由 URL 标识。

您可以创建到 URL 的 HTTP 连接,从 URL 创建集线器连接,将事件侦听器添加到集线器连接(系统事件,例如关闭和集线器消息),然后开始从 URL 接收消息,还可以开始发送消息。

在设置了 SignalR 之后,我们现在将看到如何托管集线器。

托管中心

集线器是信号员用来让客户在一个众所周知的位置聚集在一起的概念。在客户端,它被标识为一个 URL,如http://<servername>/chat。在服务器上,它是从Hub继承的类,必须在 ASP.NET Core 管道中注册。下面是聊天中心的一个简单示例:

public class ChatHub : Hub
{
    public async Task Send(string message)
    {
        await this.Clients.All.SendAsync("message", this.Context.User.
        Identity.Name, message);
    }
}

Send消息只能由 JavaScript 调用。这个Send方法是异步的,因此我们必须在一个众所周知的端点中注册这个集线器,在Configure方法中,我们注册端点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("chat");
});

您可以选择您想要的任何名称,而不需要受中心类名称的限制。

您也可以通过ConfigureServices方式注册其服务,如下所示:

services.AddSignalR();

Hub类公开了两个虚拟方法OnConnectedAsyncOnDisconnectedAsync,每当客户端连接或断开连接时,都会触发这两个方法。OnDisconnectedAsyncException为参数,只有在断开时发生错误时才不为空。

要调用hub实例,我们必须首先从 JavaScript 初始化 SignalR 框架,为此,我们需要引用~/lib/signalr/signalr.js文件(用于开发)或~/lib/signalr/signalr.min.js(用于生产)。实际代码如下所示:

var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
var httpConnection = new signalR.HttpConnection('/chat', { logger: logger });

这里,我们在同一个主机上调用一个名为chat的端点,该主机提供请求服务。现在,在客户端和服务器本身的通信方面,我们需要start它:

var connection = new signalR.HubConnection(httpConnection, logger);

connection
    .start()
    .catch((error) => {
        console.log('Error creating the connection to the chat hub');
});

如您所见,start方法返回一个承诺,您还可以链接一个catch方法调用,以便在连接时捕获任何异常。

我们可以通过挂接到onclose事件检测到连接意外关闭:

connection.onclose((error) => {
    console.log('Chat connection closed');
});

连接成功后,您将钩住自定义事件("message")

connection.on('message', (user, message) => {
    console.log(`Message from ${user}: ${message}`);
});

使用事件名称("message")on的调用应与SendAsync方法中在集线器上调用的事件名称相匹配。它还应采用相同数量(和类型)的参数。

我还使用了arrow函数,这是现代 JavaScript 的一个特性(您可以通过阅读本文了解更多信息)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions 。这只是语法,可以通过匿名函数实现。

作为说明,您可以传递其他查询字符串参数,这些参数稍后可能会在中心中捕获。还有另一种方法,使用HubConnectionBuilder

var connection = new signalR.HubConnectionBuilder()
    .configureLogging(signalR.LogLevel.Information)
    .withUrl('/chat?key=value')
    .build();

它有时很有用,因为除了调用其方法之外,将数据从客户机传递到集线器的方法不多。检索这些值的方法如以下代码所示:

var value = this.Context.GetHttpContext().Request.Query["key"].SingleOrDefault();

现在,我们可以开始发送消息:

function send(message) {
    connection.invoke('Send', message);
}

本质上,该方法异步调用ChatHub类的Send方法,任何响应都将由'message'侦听器接收,我们之前注册过该侦听器(请参见hub.on('message')

简而言之,流程如下所示:

  1. 使用集线器地址(signalR.HttpConnection)在客户端(signalR.HubConnection上创建连接,该地址必须映射到从Hub继承的.NET 类。
  2. 为某个事件(connection.on())添加了事件处理程序。
  3. 连接已启动(start()
  4. 客户端使用在Hub类上声明的方法的名称将消息发送到集线器(connection.invoke()
  5. Hub类上的方法向所有/部分连接的客户端广播消息。
  6. 当客户端收到消息时,它会向该事件名称的所有订阅者引发一个事件(与connection.on()中声明的相同)。

The client can call any method on the Hub class, and this, in turn, can raise any other event on the client.

但首先,让我们看看如何定义客户机和服务器之间的协议,以便两者可以对话。

通信协议的选择

signer 需要让客户端和服务器使用相同的语言—协议。它支持以下通信协议(或消息传输,如果您愿意):

  • WebSocket:在支持它的浏览器中,这可能是性能最好的,因为它是基于二进制的,而不是基于文本的。阅读更多关于 WebSocket 的信息:https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
  • 服务器发送事件:这是另一个 HTTP 标准;它允许客户端不断轮询服务器,给人一种服务器直接与之通信的印象;参见https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
  • AJAX 长轮询:也称为AJAX Comet,这是一种技术,通过该技术,客户端可以使一个 AJAX 请求保持活动状态,可能持续很长一段时间,直到服务器提供应答为止,即当它返回并发出另一个长请求时。

通常,signalR确定要使用的最佳协议,但您可以从客户端强制使用:

var connection = new signalR.HubConnectionBuilder()
  .configureLogging(signalR.LogLevel.Information)
  .withUrl('/chat', { skipNegotiation: false, transport: signalR.
  TransportType.ServerSentEvents })

这也可以从服务器检索,但一般来说,建议对所有协议保持打开状态:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<ChatHub>("chat", opt =>
    {
        opt.Transports = HttpTransportType.ServerSentEvents;
    });
});

例如,在不支持 WebSocket 的操作系统(如 Windows 7)中,可能需要强制使用一个协议。也可能是因为路由或防火墙可能不允许某些协议,例如 WebSocket。客户端和服务器端配置必须匹配,也就是说,如果服务器没有启用特定的协议,那么在客户端设置它将不起作用。如果您不限制运输,信号员将尝试所有这些方法,并自动选择最有效的方法。如果存在某种限制,例如客户端和服务器之间的协议不兼容,则可能需要选择特定的协议。如果你这样做了,别忘了也跳过谈判,因为这会节省一些时间。

Do not restrict the transport types unless you have a very good reason for doing so, such as browser or operating system incompatibilities.

我们已经了解了如何配置连接,现在让我们看看如何在出现故障时自动重新连接。

自动重联

您可以捕获close事件并对其作出响应,或者在连接意外断开时让信号器自动重新连接(这些事情发生在互联网上,您知道!)。为此,在客户端代码上调用withAutomaticReconnect扩展方法:

var connection = new signalR.HubConnectionBuilder()
    .withAutomaticReconnect()
    .withUrl('/chat')
    .build();

调用此方法时可以不使用参数,也可以使用表示每次尝试重新连接之前等待的时间(以毫秒为单位)的数值数组。默认值为[0, 2000, 10000, 30000, null],即首先立即尝试,然后等待两秒钟,然后等待一秒钟,然后等待三秒钟,然后停止尝试(null。第三个选项是一个函数,它接受一些参数并返回下一个延迟,如本例所示:

var connection = new signalR.HubConnectionBuilder()
    .withAutomaticReconnect({
        nextRetryDelayInMilliseconds: (retryContext) => {
            //previousRetryCount (Number)
            //retryReason (Error)
            return 2 * retryContext.elapsedMilliseconds;
        })
    .build();

在本例中,我们返回一个对象,该对象使用 lambda 函数,该函数将上一次返回计数、重试原因(异常)和自上次重试以来经过的时间作为其参数,如果要取消重新连接,则希望您返回下一次延迟或 null。

使用自动重新连接时,会引发一些事件:

connection.onreconnecting((error) => {
    //a reconnect attempt is being made
});

connection.onreconnected((connectionid) => {
    //successful reconnection
});

这些事件不言自明:

  • 当信号器由于错误而试图重新连接时,会引发onreconnecting,该错误作为其唯一参数传递给回调
  • 成功重新连接后引发onreconnected,并将新连接 ID 传递给回调

发送到集线器和从集线器发送的消息需要序列化,这是下一个主题的主题。

消息序列化

开箱即用,signer 以明文 JSON 发送消息,但还有一种替代方法,即MessagePack这是一种压缩格式,可以提供更好的性能,特别是对于更大的有效负载。

如前所述,我们需要安装@aspnet/signalr-protocol-msgpacknpm 包和Microsoft.AspNetCore.SignalR.Protocols.MessagePackNuGet 包。

我们可以指定MessagePack协议的示例如下:

var connection = new signalR.HubConnectionBuilder()
    .withUrl('/chat')
    .withHubProtocol(new signalR.protocols.msgpack.
     MessagePackHubProtocol())
    .build();

如果您选择使用MessagePack,在注册信号机服务时还需要添加对MessagePack的支持:

services
    .AddSignalR()
    .AddMessagePackProtocol();

现在,我们已经了解了如何开始对话,让我们看看信号器上下文,在这里我们可以从当前信号器会话中获取信息。

探索信号员语境

信号器上下文帮助我们了解我们在哪里以及谁在发出请求。它通过Hub类的Context属性提供。在其中,您将发现以下属性:

  • ConnectionHubConnectionContext:这是低级连接信息;您可以从它(GetHttpContext()中获取对当前HttpContext的引用以及元数据内容(Metadata,并且可以终止连接(Abort()
  • ConnectionIdstring):这是唯一一个唯一标识此集线器上客户端的连接 ID。
  • UserClaimsPrincipal):这是当前用户(使用身份验证时有用)及其所有声明。

Context属性在调用hub方法时可用,包括OnConnectedAsyncOnDisconnectedAsync。不要忘记,对于上下文,用户总是通过其ConnectionId标识;只有使用身份验证,它才会与用户名(User.Identity.Name关联。

如果我们需要向中心传递任意参数?下一个!

使用查询字符串

通过RequestQuery集合,可以在服务器端访问 URL 上传递的任何查询字符串参数(如"/chat?key=value"

var value = Context.GetHttpContext().Request.
Query["key"].SingleOrDefault();

现在,让我们了解消息可以发送到哪些实体。

了解消息目标

信号器消息可发送至以下任何一个:

  • 全部:所有连接的客户端都会收到
  • :只有某一组的客户才会收到;组由名称标识
  • 之外的组:除特定客户端以外的特定组中的所有客户端,由其连接 ID 标识
  • 客户端:只有特定的客户端,由其连接 ID 标识

客户端由连接 ID 标识,该 ID 可从Hub类的Context属性中获取:

var connectionId = this.Context.ConnectionId;

用户可以添加到任意数量的组(当然也可以删除):

await this.Groups.AddAsync(this.Context.ConnectionId, "MyFriends");
await this.Groups.RemoveAsync(this.Connection.ConnectionId, "MyExFriends");

要向组发送消息,请将All属性替换为Group调用:

await this.Clients.Group("MyFriends").InvokeAsync("Send", message);

或者,类似地,对于特定客户端,请使用以下命令:

await this.Clients.Client(this.Context.ConnectionId).InvokeAsync("Send", message);

组由信号器在内部维护,但是,当然,没有什么可以阻止您拥有自己的助手结构。这就是为什么:

  • 要向所有连接的客户端(集线器)发送消息,请执行以下操作:
await this.Clients.All.SendAsync("message", message);
  • 要仅将消息发送到由其连接 ID 标识的特定客户端,请使用以下命令:
await this.Clients.Client("<connectionid>").SendAsync("message", message);
  • 对于命名组,我们可以使用以下方法:
await this.Clients.Group("MyFriends").SendAsync("message", message);
  • 或仅针对组,可使用以下内容:
await this.Clients.Groups("MyFriends", "MyExFriends").SendAsync("message", message);
  • 对于组中除一个或两个连接 ID 以外的所有成员,我们使用以下方法:
await this.Clients.GroupExcept("MyFriends", "<connid1>", "<connid2>").SendAsync("message", message);

如果我们需要从 web 应用外部与中心通信,该怎么办?这就是下一节的主题。

从外部沟通

正如您所想象的,可以与集线器通信,这意味着向集线器发送消息。有两种可能性:

  • 来自同一 web 应用
  • 来自不同的应用

让我们逐一研究一下。

来自同一 web 应用的通信

可以从信号机外部向集线器发送消息。这并不意味着访问ChatHub类的实例,而是只访问其连接的客户端。您可以使用内置的依赖注入框架注入IHubContext<ChatHub>实例,如下所示:

public class ChatController : Controller
{
    private readonly IHubContext<ChatHub> _context;

    public ChatController(IHubContext<ChatHub> context)
    {
        this._context = context;
    }

    [HttpGet("Send/{message}")]
    public async Task<IActionResult> Send(string message)
    {
        await this._context.Clients.All.SendAsync("message", this
       .User.Identity.Name, message);
    }
}

如您所见,您负责将所有参数发送到客户端。当然,也可以发送到组或直接发送到客户端。

假设您想向所有客户发送一条重复消息;您可以在Configure方法中编写这样的代码(或者从您引用服务提供商的地方):

public class TimerHub : Hub
{
    public async Task Notify()
    {
        await this.Clients.All.SendAsync("notify");
    }
}

//in Configure
TimerCallback callback = (x) =>
{
    var hub = app.ApplicationServices.GetService<IHubContext<TimerHub>>();
     hub.Clients.All.SendAsync("notify");
};

var timer = new Timer(callback);
timer.Change(
    dueTime: TimeSpan.FromSeconds(0), 
    period: TimeSpan.FromSeconds(1));

前面的代码显示timerHubnotify事件的注册。引发事件时,会向控制台写入一条消息。如果启动订阅时发生错误,也会记录该错误。

Timer将每秒发射一次,并将当前时间广播给一个假设的TimerHub类。此TimerHub类需要注册为端点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHub<TimerHub>("timer");
});

它还需要在客户端注册:

var notificationConnection = new signalR.HubConnectionBuilder()
    .withUrl('/timer')
    .withAutomaticReconnect()           
    .configureLogging(signalR.LogLevel.Information)
    .build();

notificationConnection.on('notify', () => {
    console.log('notification received!');
});

notificationConnection
    .start()
    .catch((error) => {
        console.log(`Error starting the timer hub: ${error}`);
     });

接下来,让我们看看如何从不同的应用进行通信

从不同的应用进行通信

这是一种不同的方法:我们需要实例化一个连接到承载 SignalR hub 的服务器的客户端代理。我们需要Microsoft.AspNet.SignalR.ClientNuGet 套餐。HubConnectionBuilder用于实例化HubConnection,如下图所示:

var desiredProtocols = HttpTransportType.WebSockets | HttpTransportType.LongPolling | 
    HttpTransportType.ServerSentEvents;

var connection = new HubConnectionBuilder()
    .WithUrl("https://<servername>:5000/chat?key=value", options =>
    {
        options.Transports = desiredProtocols;
    })
    .WithAutomaticReconnect()
    .ConfigureLogging(logging => 
    {
        logging.SetMinimumLevel(LogLevel.Information);
        logging.AddConsole();
    })
    .AddMessagePackProtocol()
    .Build();

connection.On<string>("message", (msg) =>
{
    //do something with the message
});

connection.Closed += (error) =>
{
    //do something with the error
};

await connection.StartAsync();
await connection.SendAsync("message", message);

这个例子做了几件事:

  • 定义可接受的通信协议(WebSocket、长轮询和服务器发送事件)
  • 注册两个事件处理程序(ClosedOn("message")
  • 使用MessagePack协议创建连接,将重新连接、日志记录设置为Information和控制台,并传递查询字符串值"key"="value"
  • 异步启动连接
  • 调用集线器上的Send方法,向其传递字符串消息

可以从对承载 SignalR hub 的服务器具有 HTTP 访问权限的任何位置调用此代码。注意所需协议的设置以及WithAutomaticReconnectAddMessagePackProtocol扩展方法。AddConsole扩展方法来自Microsoft.Extensions.Logging.ConsoleNuGet 包。

我们已经了解了如何从托管它的应用的外部将消息发送到 SignalR hub。以下主题解释了身份验证如何与 SignalR 一起工作。

使用用户身份验证

SignalR 使用与封装 web 应用相同的用户身份验证机制,这意味着如果用户通过该应用的身份验证,则该用户将通过 SignalR 的身份验证。也可以在每次请求时发送 JWT 令牌,操作如下:

var connection = new signalR.HubConnectionBuilder()
    .withUrl('/chat', { accessTokenFactory: () => '<token>' })
    .build();

注意accessTokenFactory参数;这里,我们传递一个 lambda(它可能是一个函数),它返回一个 JWT 令牌。在客户端代码中,如果您从外部应用调用 SignalR,则需要执行以下操作:

var connection = new HubConnectionBuilder()
    .WithUrl("http://<servername>/chat", options =>
    {
        options.AccessTokenProvider = () => Task.FromResult("<token>");
    })
    .Build();

就 SignalR 而言,用户的身份由其连接 ID 决定。因此,根据您的要求,您可能需要在该 ID 和应用使用的用户 ID 之间建立映射。

我们已经了解了如何启用身份验证;现在让我们看看如何记录信号器的工作。

登录中

日志记录可以帮助我们诊断故障,并实时了解系统中发生的情况。我们可以在配置中或通过代码为 SignalR 启用详细的日志记录。对于第一个选项,将最后两行添加到您的appsettings.json文件中(对于"Microsoft.AspNetCore.SignalR""Microsoft.AspNetCore.Http.Connections"

{
    "Logging": {
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information",
            "Microsoft.AspNetCore.SignalR": "Trace",
            "Microsoft.AspNetCore.Http.Connections": "Trace"
        }
    }
}

对于后者,请将配置添加到引导代码:

public static IHostBuilder CreateHostBuilder(string[] args) => 
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(builder =>
        {
            builder
                .ConfigureLogging(logging =>
                {
                    logging.AddFilter("Microsoft.AspNetCore.SignalR", 
                    LogLevel.Trace);            
                    logging.AddFilter("Microsoft.AspNetCore.Http.
                    Connections", LogLevel.Trace);
                 })
                .UseStartup<Startup>();
        });

请注意,这只是为了启用使代码输出调试信息的标志。Trace是最详细的级别,因此它将输出几乎所有内容,包括低级网络调用。要真正进行日志记录,您需要为服务器端代码或您自己的自定义提供程序添加日志记录程序,如控制台(请注意,这只是一个示例):

.ConfigureLogging(logging => {
    logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Trace)
    logging.AddFilter("Microsoft.AspNetCore.Http.Connections", 
    LogLevel.Trace);

    logging.AddConsole();
    logging.AddProvider(new MyCustomLoggingProvider());
})

对于客户端,您需要注册一个接受两个参数的自定义函数:

function myLog(logLevel, message) {
    //do something
}

var connection = new signalR.HubConnectionBuilder()
    .configureLogging({ log: myLog })
    .withUrl('/chat')
    .build();

第一个参数logLevel是一个表示可能的日志级别之一的数字:

  • signalR.LogLevel.Critical5
  • signalR.LogLevel.Error4
  • signalR.LogLevel.Warning3
  • signalR.LogLevel.Information2
  • signalR.LogLevel.Debug1
  • signalR.LogLevel.Trace0:一切,包括网络消息

第二个参数message是描述事件的文本消息。

在本节中,我们了解了如何以不同的粒度级别在客户端和服务器端启用日志记录。

总结

在本章中,我们看到可以使用 signar 执行 AJAX 用于调用服务器端代码和异步获取响应的任务。其优点是,当服务器需要广播某些信息时,您可以使用它让服务器自己接触连接的客户端。

SignalR 是一种非常强大的技术,因为它本质上适应您的服务器和客户端支持。它使服务器到客户端的通信变得轻而易举。虽然当前版本不是发布质量,但它足够稳定,可以在项目中使用。

signar 的一些高级方面,比如流式处理或集群,还没有讨论过,因为它们更多的是一本专门的书。

本书即将结束,因此,在下一章中,我们将研究一些在前几章中未涉及的 API。

问题

您现在应该能够回答以下问题:

  1. Signal 支持哪两种序列化格式化程序?
  2. 信号员支持什么运输?
  3. MessagePack协议的好处是什么?
  4. 我们可以向哪些目标发送消息?
  5. 为什么我们要限制信号员使用交通工具?
  6. 我们是否可以从承载 Signal 的 web 应用外部向其发送消息?
  7. 我们可以用信号器进行身份验证吗?