ASP.NET Core SignalR 是一个开放源代码库,可用于简化向应用添加实时 Web 功能。 实时 Web 功能使服务器端代码能够将内容推送到客户端。
SignalR 支持以下用于处理实时通信的技术(按正常回退的顺序):
WebSockets
Server-Sent Events
长轮询
SignalR 自动选择服务器和客户端能力范围内的最佳传输方法。
Server
- 创建一个新的ASP.NET Core Web Api工程, 工程名:SignalRServer, .net 版本选5.0
.Net 6.0 拿掉了Startup,启动项的写法有所不同,这个稍后再介绍。 -
Nuget添加SignalR引用
-
添加新的文件夹Service, 在Services下添加新的接口文件
ISignalRHub.cs
在此接口文件里定义我们想在SignalR Client端要接收的消息1 2 3 4 5 6 7
namespace SignalRServer.Services { public interface ISignalRHub { Task ReceiveMessage(string data); } }
-
在Services下添加新的类
SignalRHub.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
using Microsoft.AspNetCore.SignalR; namespace SignalRServer.Services { public class SignalRHub : Hub<ISignalRHub> { public void GenerateData() { var stopwatch = new Stopwatch(); stopwatch.Start(); Clients.Caller.ReceiveMessage("Start to receive data from SignalR Server."); int progressPercentage = 0; var random = new Random(); for (int i = 10; i > 0; i--) { int waitTimeMilliseconds = random.Next(100, 2500); Thread.Sleep(waitTimeMilliseconds); progressPercentage = progressPercentage + 10; Clients.Caller.ReceiveMessage(progressPercentage.ToString()); } stopwatch.Stop(); Clients.Caller.ReceiveMessage("End to receive data from SignalR Server."); } } }
-
修改
Startup.cs
,添加SignalR的引用,并配置CORS1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder .WithOrigins("http://localhost:4200") // the Angular app url .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "SignalRServer", Version = "v1" }); }); services.AddSignalR(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SignalRServer v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseCors("CorsPolicy"); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub<SignalRHub>("/signalrdemo"); }); }
Client
- 创建新的Angular工程
ng new SignalRClient
- 添加SignalR安装包
npm install @microsoft/signalr
- 添加新的Service
ng g service services/SignalR
-
在SignalR service里添加引用
1 2
import * as signalR from '@microsoft/signalr'; import { ReplaySubject } from 'rxjs';
-
添加
SignalR
创建,关闭和Invoke方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
export class SignalRService { readonly APIUrl = 'https://localhost:44349/signalrdemo'; connection: signalR.HubConnection | undefined; receiveMessage: ReplaySubject<string> | undefined; constructor() { this.receiveMessage = new ReplaySubject<string>(); } public initiateSignalrConnection(): Promise<void> { console.log("Begin to initialize Signalr"); return new Promise((resolve, reject) => { this.connection = new signalR.HubConnectionBuilder() .withUrl(this.APIUrl) .build(); this.SetSignalrClientMethods(); this.connection.start() .then(() => { console.log(`SignalR connection success! connectionId: ${this.connection?.connectionId}`); resolve(); }) .catch((error) => { console.log(`Signalr connection error: ${error}`); reject(); }); }); } private SetSignalrClientMethods(): void { this.connection?.on('ReceiveMessage', (data: string) => { this.receiveMessage?.next(data); }); } public InvokeGenerateData(): Promise<void> | undefined { console.log("Begin to invoke server method"); if (!this.IsSignalRConnected()) { return Promise.reject("The signalr connection is disconnected."); } return this.connection?.invoke('GenerateData'); } public CloseSignalrConnection(): void { console.log("Begin to close Signalr"); if (this.connection) { this.connection.stop(); this.connection = undefined; } this.receiveMessage = undefined; } }
-
修改
app.component.html
,添加三个button1 2 3 4 5 6
<div> <button (click)="InitializeConnection()">Initialize</button> <button (click)="ReceiveMessage()">Receive</button> <button (click)="StopConnection()">Stop</button> </div> <p></p>
-
app.component.ts
里添加调用函数1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
export class AppComponent { title = 'SignalRClient'; Message: string | undefined; constructor(public signalrService: SignalRService) { } ngOnInit(): void { this.signalrService.receiveMessage?.subscribe((data: string) => { console.log(data); this.Message += data; }); } InitializeConnection(): void { this.signalrService.initiateSignalrConnection().then(() => { this.Message = "Initialzie SignalR succeed."; }) .catch((error) => { this.Message = `Signalr connection error: ${error}`; }); } ReceiveMessage(): void { this.signalrService.InvokeGenerateData()?.then(() => { console.log("Invoke AutoDiscovery complete!!!"); }) .catch((error) => { console.log(`Invoke AutoDiscovery failed!!!! ${error}`); }); } StopConnection(): void { this.signalrService.CloseSignalrConnection(); } }
.net core 6.0 Server
.net core 6.0
拿掉了Startup.cs
,在Program.cs
中配置SignalR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using SignalRServer.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(p => p.AddPolicy("corsapp", builder =>
{
builder.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader().AllowCredentials();
}));
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors("corsapp");
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapHub<SignalRHub>("/signalrdemo");
app.Run();
指定传输协议
由于SignalR支持多种传输协议,并且是自动选择传输方式,我们可以在Server端打印出当前的连接方式
1
2
3
4
5
6
public void GenerateData()
{
var transportType = Context.Features.Get<IHttpTransportFeature>().TransportType;
Console.WriteLine(transportType);
...
}
出于某些考虑,我们可能需要强制指定传输方式
在Client端指定:
1
2
3
4
5
this.connection = new signalR.HubConnectionBuilder()
.withUrl(this.APIUrl, {
transport: signalR.HttpTransportType.WebSockets
})
.build();
也可以在Server端指定
1
2
3
4
5
6
7
8
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<SignalRHub>("/signalrdemo", configureOptions =>
{
configureOptions.Transports = HttpTransportType.ServerSentEvents;
});
});
Enalbe WebSocket on IIS
IIS从8.0版本开始支持web socket,需要手动在控制面板里enable
命令行:
1
%SystemRoot%\system32\dism.exe /online /enable-feature /featurename:IIS-WebSockets
Reference
ASP.NET Core SignalR 简介
.NET Core with SignalR and Angular – Real-Time Charts
Real-time Angular 11 Application With SignalR And .NET 5
Real-time Angular Application With SignalR And ASP .NET Core
SignalR开篇以及如何指定传输协议
ASP.NET Core SignalR 的安全注意事项
ASP.NET Core SignalR 配置
WebSocket with SSL
Websocket support is not enabled by default on IIS