使用 Angular 重新连接到现有的 SignalR (.NET CORE) 套接字

Reconnecting to an existing SignalR (.NET CORE) socket with Angular

在我的项目中,如果 Internet 连接丢失,我需要重新连接到 SignalR Web Socket。 我正在使用 Angular Ionic V4,并且安装了网络信息插件。 在插件的 "Connected" 事件中 - 我正在尝试使用原始令牌调用 this._hubConnection.start()。似乎建立了新连接,但每次我从集线器获取数据时,事件都会运行两次(或 X 次,具体取决于我重新连接的次数)。

我相信发生的事情是 SignalR 在同一个密钥下创建另一个套接字,并且当消息发送到指定的密钥时 - 它被广播到具有相同密钥的所有连接,因此重复事件。

我的问题是 - 有没有办法 "reconnect" 到现有套接字而不是 "making an additional connection"?

编辑:

尝试升级到@microsoft/signalr后,我得到的是:

chrome devtool console log

它似乎在网络离线时尝试重新连接,当网络恢复时它连接并断开连接。

我尝试调试我的 SignalR 服务器。这是在客户端调用事件的方法:

 public async Task Enqueue(int processId)
    {
        var processEnqueueAppointmentResults = await _processesService.Enqueue<object>(processId);
        await Clients.User($"PROCESS_{processId}").SendAsync("ProcessEnqueued", processEnqueueAppointmentResults);
    }

调试器进入 Enqueue 一次,在客户端 - 我收到了 4 个重复的日志(意味着事件被触发了 4 次)

由于您使用的是旧的且未维护的 @aspnet/signalr 包,我建议切换到提供 Automatic Reconnect 功能的新包 @microsof/singnalR

因此,您可以使用此功能让 SignalR 在不是错误的情况下处理丢失的连接,就像您示例断开的互联网连接一样。

您只需像这样添加此功能:

this.hubMessageConnection = new signalR.HubConnectionBuilder()
    .configureLogging(signalR.LogLevel.Error).withUrl(signalRUrl + "/yourHub",
      {
        accessTokenFactory: () => token
      })
    .withAutomaticReconnect()
    .build();

这证明可以解决您的问题。

已解决

问题是在我的 Internet 连接事件中,我调用了 createConnection() 初始化 SignalR 连接的方法

this._hubConnection = new signalR.HubConnectionBuilder()
      .withUrl(`${this.env.testUrl}/hubs/patients`, {
        accessTokenFactory: () => token
      })
      .build();

相反,只需在我现有的 hubConnection 上调用 this._hubConnection.start(); 方法即可解决重复调用(正如我所想,每个重新连接事件都在同一键下添加了另一个套接字。当 SignalR 想要调用客户端上的事件 - 它触发了我调用的次数 createConnection())

我一起编写了一个“安全调用约定”——仍然需要更通用,但想法是你在集线器上调用一个方法——如果连接断开,那么连接应该恢复并再次调用该方法。如果连接断开,并且不会恢复,请继续重新绑定,直到连接恢复,然后在打开的连接上调用该方法。

getChatSubjects(subject?: Subject<ChatSubject[]>) {

    if(!subject) {
      subject = new Subject<ChatSubject[]>();
    }
  
    let connectionState = (this.connection as any)._connectionState;
    console.log("connection state " + connectionState);

    if( connectionState == "Connected" ) {

      this.connection.invoke("GetChatSubjects").then(results => {

        this.chatSubjects = [];
        for (var i = 0; i < results.length; i++) {
          this.chatSubjects.push(results[i]);
        }
        subject.next(this.chatSubjects);
      });
    }
    else if(connectionState == "Reconnecting") {
      let message = "Connection in reconnecting state, retrying in 3 seconds";
      console.log(message)
      
      timer(3000).pipe(take(1)).subscribe(time => this.getChatSubjects(subject));
    }
    else {
      console.log("connection in a closed state")
      console.log("trying to open connection again")

      this.startConnecton().then(result => {
        
        if(!result) {
          console.log("could no re-open connection");
          console.log("will retry in 10 seconds");
          timer(10000).pipe(take(1)).subscribe(time => this.getChatSubjects(subject));
        }
        else {
          console.log("connection re-opened")
          timer(1000).pipe(take(1)).subscribe(time => this.getChatSubjects(subject));
        }
      })
    } 
  }

...

let subject = new Subject<ChatSubject[]>();
    subject.subscribe(results => {
      this.chatSubjects = results;
    })
    this.signalRService.getChatSubjects(subject)

注意源代码使用 RXJS。另请注意,我选择不在 signalR 连接上使用自动重新连接功能,因为它太“快乐”了,但仍然允许您在关闭的连接上调用方法。