The connection works fine but when I try to call a method it ends up calling the disconnect method with an erorr saying it can't find "ParseMessage". I have 2 separate .NET 7 apps. One for server and 1 for client. This is all just a test to get methods being called.
When I press the Start Timer button that's when it disconnects me.
Server hub:
using Microsoft.AspNetCore.SignalR;
namespace SignalRTest;
public class MyHub : Hub
{
// This dictionary could map user connections to active timers if needed for multiple users
private static readonly Dictionary<string, CancellationTokenSource> UserTimers = new Dictionary<string, CancellationTokenSource>();
// This method is called when a client successfully connects to the hub.
public override async Task OnConnectedAsync()
{
// You can retrieve the connection ID or perform any startup logic here
string connectionId = Context.ConnectionId;
Console.WriteLine($"Client connected: {connectionId}");
// You can also send a message back to the client on connection
await Clients.Client(connectionId).SendAsync("ReceiveMessage", $"Welcome! Your connection ID is {connectionId}");
// Call the base class method to ensure proper connection handling
await base.OnConnectedAsync();
}
// This method is called when a client disconnects from the hub.
public override async Task OnDisconnectedAsync(Exception? exception)
{
string connectionId = Context.ConnectionId;
Console.WriteLine($"Client disconnected: {connectionId}");
if (exception != null)
{
Console.WriteLine($"Disconnected due to an error: {exception.Message}");
}
if (UserTimers.ContainsKey(connectionId))
{
UserTimers[connectionId].Cancel();
UserTimers.Remove(connectionId);
}
// Call the base class method to ensure proper disconnection handling
await base.OnDisconnectedAsync(exception);
}
// Method called by the client to start the timer
public async Task StartTimer()
{
var connectionId = Context.ConnectionId;
// Ensure only one timer per connection
if (UserTimers.ContainsKey(connectionId))
{
await Clients.Caller.SendAsync("ReceiveMessage", "Timer already running.");
return;
}
// Create a cancellation token for this user's timer
var cts = new CancellationTokenSource();
UserTimers[connectionId] = cts;
await Clients.Caller.SendAsync("ReceiveMessage", "Timer started.");
// Start a loop to send updates every 5 seconds
int counter = 0;
while (!cts.Token.IsCancellationRequested)
{
counter += 5;
await Clients.Caller.SendAsync("ReceiveTimerUpdate", counter);
try
{
await Task.Delay(5000, cts.Token); // Wait 5 seconds
}
catch (TaskCanceledException)
{
break; // If the task is canceled, exit the loop
}
}
}
// Method called by the client to stop the timer
public async Task StopTimer()
{
var connectionId = Context.ConnectionId;
if (UserTimers.ContainsKey(connectionId))
{
UserTimers[connectionId].Cancel();
UserTimers.Remove(connectionId);
await Clients.Caller.SendAsync("ReceiveMessage", "Timer stopped.");
}
else
{
await Clients.Caller.SendAsync("ReceiveMessage", "No active timer found.");
}
}
}
Client Index.html
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SignalR Timer Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.js"></script>
</head>
<body>
<button id="startButton">Start Timer</button>
<button id="stopButton">Stop Timer</button>
<div id="timerOutput"></div>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("https://localhost:7156/my-hub", {
withCredentials: true // Allows credentials like cookies or authentication tokens to be sent
})
.withAutomaticReconnect()
.build();
connection.start().then(() => {
console.log("Connected to SignalR");
}).catch(err => console.error(err));
document.getElementById("startButton").addEventListener("click", () => {
connection.invoke("StartTimer").catch(err => console.error(err));
});
document.getElementById("stopButton").addEventListener("click", () => {
connection.invoke("StopTimer").catch(err => console.error(err));
});
connection.on("ReceiveTimerUpdate", (counter) => {
document.getElementById("timerOutput").innerText = `Time: ${counter} seconds`;
});
connection.on("ReceiveMessage", (message) => {
console.log(message);
});
</script>
</body>
</html>
With logging enabled the server shows this when I click StartTimer button:
Client side when logging added and I press StartTimer button
This is a server side error when a code in one assembly tries to execute method in another assembly. According to the message it is not related to SignalR itself. The connection is closed because of server side exception. This could be because of inconsistent packages versions. I'd suggest to check if the major versions are 7 for both the Microsoft.AspNetCore.SignalR.Common and Microsoft.AspNetCore.SignalR.Protocols.Json packages.