I am trying to notify all connected clients before rebooting the server (using the standard button in IIS 10), but the clients do not receive any message, only disconnection.
This code does not give errors but does not work:
public sealed class MyService : BackgroundService
{
...
public override async Task StopAsync(CancellationToken cancellationToken)
{
Task t = _myHubDI.Clients // IHubContext<MyHub>, not null
.Group("ALL")
.SendAsync("qwerty1234");
await t;
var s = t.Status; // RanToCompletion
var e = t.Exception; // null
await base.StopAsync(cancellationToken);
}
...
}
The Stop
button will kill w3wp.exe
process directly, This means that the application does not have a chance to perform any cleanup logic, including events in IHostApplicationLifetime.
So if you want to notify all signalr clients, we have to manually click Recyle...
button in the Application Pool
, and then return to the site and click Stop
button again to achieve this requirement.
Here is the test result.
Test Code
Index.cshtml
@{
ViewData["Title"] = "Home Page";
}
<!DOCTYPE html>
<html>
<head>
<title>SignalR Client</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/7.0.5/signalr.min.js"></script>
</head>
<body>
<h1>SignalR Client</h1>
<p id="message">Waiting for server stop message...</p>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/myhub")
.build();
connection.on("qwerty1234", function (message) {
document.getElementById("message").innerText = "receive information:" + message;
});
connection.start()
.then(() => {
console.log("connected!");
connection.invoke("AddToGroup", "ALL").catch(err => {
console.error("failed to add the group:" + err);
});
})
.catch(err => {
console.error("failed to connect:" + err);
});
</script>
</body>
</html>
MyHub.cs
using Microsoft.AspNetCore.SignalR;
namespace _79393494
{
public class MyHub : Hub
{
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
}
}
MyService.cs
using _79393494;
using Microsoft.AspNetCore.SignalR;
public sealed class MyService : BackgroundService
{
private readonly IHubContext<MyHub> _myHubDI;
private readonly IHostApplicationLifetime _appLifetime;
private readonly ILogger<MyService> _logger;
public MyService(IHubContext<MyHub> myHubDI, IHostApplicationLifetime appLifetime, ILogger<MyService> logger)
{
_myHubDI = myHubDI;
_appLifetime = appLifetime;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_appLifetime.ApplicationStopping.Register(async () =>
{
_logger.LogInformation("The application is shutting down, notifying clients...");
try
{
await _myHubDI.Clients.Group("ALL").SendAsync("qwerty1234", "The server is stoping, please save your data!");
_logger.LogInformation("Notification has sent to client");
await Task.Delay(2000);
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while sending the notification."+ ex.Message);
}
});
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Background service(MyService) is running...");
await Task.Delay(5000, stoppingToken);
}
}
}
Program.cs
...
builder.Services.AddSignalR();
builder.Services.AddHostedService<MyService>();
...
app.MapHub<MyHub>("/myhub");
...