You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SignalR HubConnection无法更新服务器推送数据的WinForms客户端问题

问题

服务器端通过Background Service + SignalR Hub向所有客户端推送实时时间,客户端基于WinForms开发。

  • 服务器日志显示时间推送正常:
Worker running at: 08/16/2023 14:44:29
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:30
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:31
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:32
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:33
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:34
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:35
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:36
info: Pr.WebApi.HostedServices.TimeHostedService[0]
      Worker running at: 08/16/2023 14:44:37
  • 客户端输出始终显示启动时的固定时间(启动时间14:43:17):
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM
8/16/2023 2:43:17 PM

客户端核心代码如下:

private async void ClientForm_Load(object sender, EventArgs e)
{
     await Task.Run(() => ExecuteAsync());
}

public async Task ExecuteAsync()
{
    while (true)
    {
        try
        {
            if (connection.State != HubConnectionState.Connected)
            {
                await connection.StartAsync();

                ConnectionStatusLabel.Invoke(delegate
                        {
                            ConnectionStatusLabel.Text = "Connected";
                            ConnectionStatusLabel.ForeColor = Color.FromArgb(0, 225, 50);
                        });
            }

            connection.On<DateTime>("SendTime", e =>
                    {
                        TimeShowLabel.Invoke(delegate
                        {
                            TimeShowLabel.Text = e.ToString("HH:mm:ss");
                            Debug.WriteLine(e.ToString());
                        });
                    });
        }
        catch (Exception ex)
        {
            ConnectionStatusLabel.Invoke(delegate
                    {
                        ConnectionStatusLabel.Text = "Not connected";
                        ConnectionStatusLabel.ForeColor = Color.Red;
                    });
            Console.WriteLine(ex.Message);
            Thread.Sleep(1000);
        }
    }
}

若将await connection.StartAsync();移出if(connection.State != HubConnectionState.Connected)判断块,客户端可正常更新时间,但会持续抛出System.InvalidOperationException异常:

Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll

Pr.WebApi.HostedServices.TimeHostedService: Information: Worker running at: 08/16/2023 14:48:32

Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll

Pr.WebApi.HostedServices.TimeHostedService: Information: Worker running at: 08/16/2023 14:48:33

Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll

需要实现无异常且能正确更新时间的功能。

解决方案

问题根源

  1. 重复注册回调:connection.On<DateTime>("SendTime", ...)被放在无限循环中执行,每次循环都会注册一个新的回调函数。当服务器推送时间时,所有注册的回调都会触发,但早期的回调可能捕获了初始时间值,导致显示固定时间。
  2. 不必要的重复启动连接:移出判断块后反复调用connection.StartAsync(),而连接已处于Connected状态,触发InvalidOperationException。

修复后的代码

private async void ClientForm_Load(object sender, EventArgs e)
{
    // 仅注册一次回调,避免重复注册导致异常行为
    connection.On<DateTime>("SendTime", e =>
    {
        TimeShowLabel.Invoke(delegate
        {
            TimeShowLabel.Text = e.ToString("HH:mm:ss");
            Debug.WriteLine(e.ToString());
        });
    });

    await ExecuteAsync();
}

public async Task ExecuteAsync()
{
    while (true)
    {
        try
        {
            // 仅当连接完全断开时才尝试重连
            if (connection.State == HubConnectionState.Disconnected)
            {
                await connection.StartAsync();

                ConnectionStatusLabel.Invoke(delegate
                {
                    ConnectionStatusLabel.Text = "Connected";
                    ConnectionStatusLabel.ForeColor = Color.FromArgb(0, 225, 50);
                });
            }
            // 连接正常时短暂休眠,避免空循环占用CPU
            await Task.Delay(1000);
        }
        catch (Exception ex)
        {
            ConnectionStatusLabel.Invoke(delegate
            {
                ConnectionStatusLabel.Text = "Not connected";
                ConnectionStatusLabel.ForeColor = Color.Red;
            });
            Console.WriteLine(ex.Message);
            // 重连间隔1秒
            await Task.Delay(1000);
        }
    }
}

关键调整点

  • 将connection.On回调注册移到循环外部,仅执行一次,避免重复注册导致的异常行为。
  • 连接状态判断更精确:仅当连接处于Disconnected状态时才尝试重连,避免对Connected/Connecting状态的连接执行StartAsync。
  • 用await Task.Delay(1000)替代Thread.Sleep(1000),避免阻塞异步线程,更符合异步编程规范。
  • 移除Task.Run:ExecuteAsync本身是异步方法,直接await即可,若担心阻塞UI线程可保留Task.Run但内部逻辑不变。

内容的提问来源于stack exchange,提问作者Duane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 01:52:04