视频演示地址:
一、概念
1、什么是 SignalR?
ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。
2、适合 SignalR 的场景
- 需要从服务器进行高频率更新的应用。 示例包括游戏、社交网络、投票、拍卖、地图和 GPS 应用。
- 仪表板和监视应用。 示例包括公司仪表板、即时销售更新或旅行警报。
- 协作应用。 协作应用的示例包括白板应用和团队会议软件。
- 需要通知的应用。 社交网络、电子邮件、聊天、游戏、旅行警报和很多其他应用都需使用通知。
3、SignalR实时通信使用的传输技术(以下为使用的先后选择顺序,自动选择服务器和客户端能力范围内的最佳传输方法)
- WebSockets
- Server-Sent Events
- 长轮询
4、SignalR支持的功能
- 自动处理连接管理。
- 同时向所有连接的客户端发送消息。 例如聊天室。
- 向特定客户端或客户端组发送消息。
- 对其进行缩放,以处理不断增加的流量。
- SignalR中心协议(SignalR 使用 Hubs(中心协议) 在客户端和服务器之间进行通信;内置中心协议:基于JSON的文本协议 和 基于 MessagePack 的二进制协议(JSON的代替方案,消息更小), 旧版浏览器必须支持 XHR级别2 才能提供 MessagePack 协议支持。)
5、SignalR服务器与客户端标准
服务器标准:支持 ASP.NET Core 支持的任何服务器平台;如果服务器运行 IIS中,则 WebSockets 传输需要 Windows Server 2012 或更高版本上的 IIS 8.0 或更高版本。
Web客户端标准:SignalR 面向 ES6。 对于不支持 ES6 的浏览器,请将库转译为 ES5。 有关详细信息,请参阅 使用ES6入门 – 使用 Traceur和Babel 将 ES6转为ES5。
.Net客户端标准:.NET 客户端可在 ASP.NET Core 支持的任何平台上运行(Xamarin.Android版本>8.4.0.1;Xamarin.iOS版本>11.14.0.4)。
Java 客户端标准:支持 Java 8 及更高版本。
二、知识点
1、创建SignalR Server
Microsoft.AspNetCore.App已集成了Microsoft.AspNetCore.SignalR包;不需要独立安装Microsoft.AspNet.SignalR或Microsoft.AspNetCore.SignalR包
(1)添加SignalR服务
(2)启用SignalR服务
(3)创建并使用自定义Hub-ChatHub
- 自定义方法名:使用[HubMethodName("方法名")]特性标识方法;
- 可以使用DI中的服务作为参数;
- 可以使用 OnConnectedAsync 和 OnDisconnectedAsync 虚拟方法来管理和跟踪连接;
- 中心方法中引发的异常将发送到调用该方法的客户端;如下图的SendMessage被客户端调用,发生的异常会反馈给客户端。
(4)服务器中(ChatHub之外)使用ChatHub(通过IHubContext调用)
- 可以将
IHubContext
实例注入控制器、中间件或其他 DI 服务。 - 当从
Hub
类外部调用客户端方法时,没有与该调用关联的调用方。 因此,无法访问ConnectionId
、Caller
和Others
属性。 - 注入强类型 HubContext时(见‘(5)强类型改造’),请确保中心继承自
Hub<T>
。 使用IHubContext<THub, T>
接口而不是IHubContext<THub>
进行注入。 - 在泛型代码中使用 IHubContext时,注入的 IHubContext<THub> 实例可以强制转换为 IHubContext,而无需指定泛型 Hub 类型。
2、创建SignalR Client
- 客户端断线重连后,会被赋予一个新的
ConnectionId
,可通过Reconnected
事件获取。(注:如果 HubConnection 已配置为跳过协商,则Reconnected 事件处理程序的 connectionId 参数会为 null) - 若使用重连方法,推荐异步进行。
(1).Net版
见“三-5”示例
(2)Javascript版
见“三-1”示例
3、Hub知识点
(1) Hub. Context(用户标识:Context.UserIdentifier)
类 Hub 包含一个 Context 属性,该属性包含以下属性以及有关连接的信息:
属性 | 说明 |
获取连接的唯一 ID(由 SignalR 分配)。 每个连接都有一个连接 ID。 | |
获取 用户标识符。 默认情况下,SignalR 使用与连接关联的 ClaimsPrincipal 中的 ClaimTypes.NameIdentifier 作为用户标识符。用户标识符区分大小写。 | |
获取与当前用户关联的 ClaimsPrincipal。 | |
获取可用于在此连接范围内共享数据的键/值集合。 数据可以存储在此集合中,会在不同的中心方法调用间为连接持久保存。 | |
获取连接上可用的功能的集合。 目前,在大多数情况下不需要此集合,因此未对其进行详细记录。 | |
获取一个 CancellationToken,它会在连接中止时发出通知。 |
Hub.Context 还包含以下方法:
方法 | 说明 |
HttpContext返回连接的 ;如果连接未与 HTTP 请求关联, | |
中止连接。 |
(2) Hub.Clients
- 当从
Hub
类外部调用客户端方法时,没有与该调用关联的调用方。 因此,无法访问ConnectionId
、Caller
和Others
属性。 - 用户组
- 对组进行操作可以使用Client. Group()或Groups.AddToGroupAsync(Context.ConnectionId, groupName)、Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
- 重新连接时不会保留组成员身份。 重新建立连接后,需要重新加入组。
- 无法计算组的成员数,因为如果将应用程序扩展到多台服务器,则无法获取此信息。需要自己实现。
- 组不是一项安全功能。 身份验证声明具有组不具备的功能,例如到期和撤销。 如果撤销用户对组的访问权限,应用必须从组中显式删除该用户。
- 组名称区分大小写。
类 Hub 包含一个 Clients 属性,该属性包含以下属性,用于服务器和客户端之间的通信:
Hub.Clients 还包含以下方法:
方法 | 说明 |
对所有连接的客户端调用方法(指定连接除外) | |
对连接的一个特定客户端调用方法 | |
对连接的多个特定客户端调用方法 | |
对指定组中的所有连接调用方法 | |
对指定组中的所有连接调用方法(指定连接除外) | |
对多个连接组调用方法 | |
对一个连接组调用方法(不包括调用了中心方法的客户端) | |
对与一个特定用户关联的所有连接调用方法 | |
对与多个指定用户关联的所有连接调用方法 |
(3)向客户端发送消息(SendAsync)
(4)从客户端请求结果(示例为GetMessage方法)
① 服务器端
这要求服务器使用 ISingleClientProxy.InvokeAsync
,并且客户端从其 .On
处理程序返回结果。示例如下:
方式一:接口中使用(推荐)
方式二:写在自定义的Hub中
② 客户端
(5)对SendAsync中“调用的方法名”进行强类型改造(示例为ReceiveMessage
方法)
使用 SendAsync
的缺点是它依赖于字符串来指定要调用的客户端方法。 如果客户端中的方法名称拼写错误或缺失,则这会使代码可能出现运行时错误。我们可以使用 Hub<T>强类型 Hub类来约束自定义的Hub类(使用强类型 Hub<T> 会禁止使用 SendAsync,只提供对 接口中定义的方法的访问),如下:
(6)自定义传输对象(方法参数;增强历史版本的兼容性)
① 修改前(服务器自定义的方法)
以如下所示的服务器端 API 为例:
JavaScript 客户端使用 invoke
调用此方法,如下所示:
如果稍后将第二个参数添加到服务器方法,旧客户端不会提供此参数值。 例如:
在服务器上,你将看到如下日志消息:
旧客户端只发送了一个参数,但新的服务器 API 需要两个参数。 使用自定义对象作为参数可提供更大的灵活性。 让我们重新设计原始 API 以使用自定义对象。
② 修改后(服务器自定义的方法)
单参
现在,客户端使用对象来调用方法:
添加参数
当旧客户端发送单个参数时,额外的 Param2
属性将保留为 null
。 你可以通过检查 Param2
是否为 null
来检测旧客户端发送的消息并应用默认值。 新客户端可以发送这两个参数。
③ 补充-客户端上自定义的方法(new {内容})
从服务器端发送自定义对象:
在客户端,访问 Message
属性而不是使用参数:
如果稍后决定将消息的发送方添加到有效负载中,请向对象添加一个属性:
旧客户端不需要 Sender
值,因此会忽略它。 新客户端可以通过更新为读取新属性来接受它:
在这种情况下,新客户端也可以容忍不提供 Sender
值的旧服务器。 由于旧服务器不提供 Sender
值,因此客户端在访问它之前会检查它是否存在。
(7) 中心筛选器
- 在 ASP.NET Core 5.0 或更高版本中可用。
- 允许在客户端调用中心方法之前和之后运行逻辑。
- 中心筛选器可以全局应用或按中心类型应用。
- 筛选器的添加顺序就是其运行顺序。 全局中心筛选器在本地中心筛选器之前运行。
① 配置中心筛选器
中心筛选器有三种添加方式:
② 创建中心筛选器
- 通过声明从
IHubFilter
继承的类来创建筛选器,并添加InvokeMethodAsync
方法。 - 还可以选择实现
OnConnectedAsync
和OnDisconnectedAsync
,以分别包装OnConnectedAsync
和OnDisconnectedAsync
中心方法。 - 使用
next
方法调用下一个筛选器。 - 若要跳过筛选器中的中心方法调用,请引发
HubException
类型的异常,而不是调用next
。 如果客户端需要结果,则会收到错误。
③ 使用中心筛选器的示例
- 编写筛选器逻辑时,请尝试通过在中心方法上使用属性而不是检查中心方法名称,使其成为泛型逻辑。
以某个筛选器为例,该筛选器将检查中心方法参数中是否有被禁短语,并将找到的任何短语替换为 ***
。 对于此示例,假设定义了LanguageFilterAttribute
类。 该类有一个名为FilterArgument
的属性,可以在使用该属性时对其进行设置。
a. 将该属性放在需要清理字符串参数的中心方法上:
b. 定义一个中心筛选器,以检查该属性并将中心方法参数中的被禁短语替换为 ***
:
c. 在Startup.ConfigureServices
方法中注册中心筛选器。 为了避免每次调用都重新初始化被禁短语列表,中心筛选器将注册为单一实例:
④ HubInvocationContext 对象
HubInvocationContext
包含当前中心方法调用的信息。
属性 | 说明 | 类型 |
|
|
|
| 用于此中心方法调用的中心实例。 |
|
| 正在调用的中心方法的名称。 |
|
| 传递给中心方法的参数列表。 |
|
| 用于此中心方法调用的已限定范围的服务提供程序。 |
|
| 中心方法信息。 |
|
⑤ HubLifetimeContext 对象
HubLifetimeContext
包含 OnConnectedAsync
和 OnDisconnectedAsync
中心方法的信息。
属性 | 说明 | 类型 |
|
|
|
| 用于此中心方法调用的中心实例。 |
|
| 用于此中心方法调用的已限定范围的服务提供程序。 |
|
三、示例(聊天室):
官方示例地址: SignalR-samples(Android、WinFrom、Xamarin、UWP、Js等)
1、JavaScript使用SignalR教程
教程见: ASP.NET Core SignalR 入门(Microsoft.AspNetCore.App已集成的包;不需要独立安装Microsoft.AspNet.SignalR或Microsoft.AspNetCore.SignalR包)
代码地址: csharp_networkprotocol_signalr/SignalRChat_Js
2、TypeScript使用SignalR教程
教程见: 使用 TypeScript 和 Webpack 开始使用 ASP.NET Core SignalR
3、Blazor使用SignalR教程
教程见: 结合使用 ASP.NET Core SignalR 和 Blazor
4、WebAPI+Vue3使用SignalR教程
代码地址: csharp_networkprotocol_signalr/SignalRServer_WebAPI与 csharp_networkprotocol_signalr\signalrvue。
(1)后台代码(WebAPI)
Microsoft.AspNetCore.App已集成的包;不需要独立安装Microsoft.AspNet.SignalR或Microsoft.AspNetCore.SignalR包
① 启用SignalR(Program.cs)
② ChatHub.cs
③ 接口中使用
(2)前台代码(Vue)
① 引用signalr库
② Chat/Index.vue
5、.Net客户端使用教程SignalR-WinForm/MAUI
代码地址:
- 服务器端: csharp_networkprotocol_signalr/SignalRServer_WebAPI
- MAUI客户端: csharp_networkprotocol_signalr/SignalRClient_MauiApp
- WinForm客户端: csharp_networkprotocol_signalr/SignalRClient_WinForm
(1)服务端:
见4-(1)-WebAPI代码。
(2)客户端(WinForm版;MAUI版略,直接看代码):
① 安装Negut包:
② 连接/断开、接收功能。
④ 发送功能
⑤ 事件
6、Java版
教程见: ASP.NET Core SignalR Java 客户端
四、分布式环境中使用SignalR
1、方式一:使用Azure SignalR 服务
见: 将 ASP.NET Core SignalR 应用发布到 Azure 应用服务。
2、方式二:使用Redis集群中发布/订阅功能
见: 设置 Redis 集群以实现 ASP.NET Core SignalR 横向扩展。
五、搭建SignalR流式服务器(流式传输)
见: 在 ASP.NET Core SignalR 中使用流式传输。
六、SignalR中使用MessagePack(更小的传输载体)
见: 在 ASP.NET Core SignalR 中使用 MessagePack 中心协议。
七、常见问题:
1、报错“An attempt was made to access a socket in a way forbidden by its access permissions...”
SignalR 连接是持久的,会占用服务器的 TCP 连接数量。当TCP 连接数量用完时,你会看到这个错误(随机套接字错误和连接重置错误)。
处理对策如下:① 修改服务器的TCP连接数量限制;② 将SignalR与其他 Web 应用部署在不同的服务器上。
作者:꧁执笔小白꧂