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

WebSocket连接失败:SignalR实时位置共享功能故障求助

SignalR连接一直处于"Connecting"状态排查方案

我正在开发实时位置共享功能,当前使用的SignalR Hub用于推送正在共享位置的用户列表。点击启动按钮后,连接始终卡在"Connecting"状态,新用户共享位置的消息无法被注册。参考聊天功能教程适配了需求,但功能无法正常工作,需要排查问题。


前端代码

share-live.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs';
import { take } from 'rxjs/operators';
import { MapService } from 'src/app/services/location/map.service';
import { AccountService } from 'src/app/shared/services';
import { ShareLiveService } from '../services/share-live.service';

@Component({
  selector: 'app-share-live',
  templateUrl: './share-live.component.html',
  styleUrls: ['./share-live.component.scss']
})
export class ShareLiveComponent implements OnDestroy{
  userPosition: google.maps.LatLngLiteral = null;
  username: string;
  stopWatch = true;
  connectedToHub = false;
  isSharing = new BehaviorSubject<boolean>(false);
  id: number;
  options: google.maps.MapOptions = {
    center: {lat: 45.9432, lng: 26.5},
    zoom: 16
  };
  markerOptions: google.maps.MarkerOptions = {
    clickable: true,
    draggable: false,
  };

  markerSettings = [
    {
        visible:true,
        shape:'Circle',
        fill:'white',
        width:3,
        animationDuration:0,
        border:{width:2,color:'#333'}
   }
  ];

  constructor(
    readonly mapService: MapService,
    private readonly shareLiveService: ShareLiveService,
    private readonly accountService: AccountService
    ) {
    this.accountService.currentUserSource
      .pipe(take(1))
      .subscribe(user => this.username = user.username);
    this.positionMap();
  }

  ngOnDestroy(): void {
    if(this.connectedToHub){
      this.stopHubConnection();
    }
  }

  start(): void {
    this.stopWatch = false;
    this.isSharing.next(!this.stopWatch);
    this.connectDisconectHub();
    this.id = navigator.geolocation.watchPosition((position) => {
      console.log(position);
      this.refreshOptions(position.coords);
      this.refreshLocation(position.coords);
    });
  }

  stop(): void {
    this.stopWatch = true;
    this.connectDisconectHub();
    if(this.id){
      navigator.geolocation.clearWatch(this.id);
      this.id = null;
      this.isSharing.next(!this.stopWatch);
    }
  }

  private connectDisconectHub(): void {
    if(!this.connectedToHub){
      this.createHubConnection();
    } else {
      this.stopHubConnection();
    }
  }

  private createHubConnection(): void {
    this.shareLiveService.createHubConnection(this.username);
    this.connectedToHub = true;
  }

  private stopHubConnection(): void {
    this.shareLiveService.eraseSharer(this.username)
    .catch(error=> console.log(error));
    this.shareLiveService.stopHubConnection();
    this.connectedToHub = false;
  }

  private positionMap(): void {
    navigator.geolocation.getCurrentPosition((position) => {
      this.refreshOptions(position.coords);
    });
  }

  private refreshOptions(coordinates: GeolocationCoordinates): void {
    this.options = {
      ...this.options,
      center: {lat: coordinates.latitude, lng: coordinates.longitude}
    };
  }

  private refreshLocation(coordinates: GeolocationCoordinates): void {
    this.userPosition = {lat: coordinates.latitude, lng: coordinates.longitude};
  }
}

share-live.service.ts

import { Injectable } from '@angular/core';
import { HttpTransportType, HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
import { BehaviorSubject, Observable } from 'rxjs';
import { LocationCoords } from 'src/app/shared/types';
import { environment } from 'src/environments/environment';

@Injectable({
  providedIn: 'root'
})
export class ShareLiveService {
  hubUrl = environment.hubUrl;
  livesharerSource$: Observable<{username: string}[]>;
  private hubConnection: HubConnection;
  private livesharer = new BehaviorSubject<{username: string}[]>([]);

  constructor() {
    this.livesharerSource$ = this.livesharer.asObservable();
  }

  createHubConnection(username: string): void {
    this.hubConnection = new HubConnectionBuilder()
      .withUrl(this.hubUrl + 'locationsharers',{
        skipNegotiation: true,
        transport: HttpTransportType.WebSockets
      })
      .build();
    this.hubConnection.start()
      .then(() => this.sendNewSharer(username))
      .catch(error => console.log(error));
  }

  stopHubConnection(): void {
    if(this.hubConnection){
      this.hubConnection.stop()
      .then(() => console.log('Connection stopped!'))
      .catch(error => console.log(error));
    }
  }

  async sendNewSharer(username: string): Promise<any> {
    return this.hubConnection.invoke('AddNewSharer', username)
    .catch(error => console.log(error));
  }

  async eraseSharer(username: string): Promise<any> {
    return this.hubConnection.invoke('RemoveSharer', username)
    .catch(error => console.log(error));
  }
}

environment.ts

export const environment = {
  production: false,
  url:'https://localhost:5001/api',
  hubUrl: 'http://localhost:5001/hubs/',
  mapbox: {
    accessToken: 'token'
  }
};

后端代码

LiveLocationSharersHub.cs

using DataAccessAPI.DTOs;
using DataAccessAPI.Entities;
using DataAccessAPI.Interfaces;
using Microsoft.AspNetCore.SignalR;

namespace DataAccessAPI.SignalR
{
    public class LiveLocationSharersHub : Hub
    {
        private readonly ILiveLocationSharersRepository _liveLocationSharersRepository;
        private readonly ILiveLocationRepository _liveLocationRepository;

        public LiveLocationSharersHub(ILiveLocationSharersRepository liveLocationSharersRepository, ILiveLocationRepository liveLocationRepository)
        {
            _liveLocationSharersRepository = liveLocationSharersRepository;
            _liveLocationRepository = liveLocationRepository;
        }

        public override async Task OnConnectedAsync()
        {
            var httpContext = Context.GetHttpContext();
            await Groups.AddToGroupAsync(Context.ConnectionId, "people-sharing");

            var peopleSharing = _liveLocationSharersRepository.GetAll();

            await Clients.Group("people-sharing").SendAsync("PeopleSharingLocation", peopleSharing);
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            await base.OnDisconnectedAsync(exception);
        }

        public async Task AddNewSharer(string username)
        {
            if (!await _liveLocationRepository.UserExistsAsync(username))
            {
                throw new HubException("This user doesn't exist! Something is wrong.");
            }

            var newSharer = new LiveLocationSharer
            {
                Username = username
            };

            _liveLocationSharersRepository.AddSharer(newSharer);
            if (await _liveLocationRepository.SaveAllAsync())
            {
                await Clients.Group("people-sharing").SendAsync("NewSharer", username);
            }
            else
            {
                throw new HubException("Something went wrong.");
            }
        }

        public async Task RemoveSharer(string username)
        {
            if (!await _liveLocationRepository.UserExistsAsync(username))
            {
                throw new HubException("This user doesn't exist! Something is wrong.");
            }

            var sharer = await _liveLocationSharersRepository.GetLiveLocationSharersAsync(username);

            _liveLocationSharersRepository.Delete(sharer);
            if (await _liveLocationRepository.SaveAllAsync())
            {
                await Clients.Group("people-sharing").SendAsync("UserDeleted", username);
            }
            else
            {
                throw new HubException("Something went wrong.");
            }
        }

    }
}

后端配置代码

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebAPIv5 v1"));
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseCors(policy => policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:8100"));

    app.UseAuthentication();
    app.UseAuthorization();
    app.UseSession();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHub<LiveLocationHub>("hubs/livelocation");
        endpoints.MapHub<LiveLocationSharersHub>("hubs/locationsharers");
    });
}

排查修复要点

  1. 协议不匹配问题

    • 后端配置了app.UseHttpsRedirection()强制HTTPS,但前端environment.ts里的hubUrl使用http协议,导致WebSocket握手被重定向失败。
    • 修复:将hubUrl改为https://localhost:5001/hubs/,与API地址协议保持一致。
  2. CORS配置缺失WebSocket支持

    • 当前CORS配置未允许凭据传递,WebSocket连接可能因权限问题失败。
    • 修复:修改CORS配置:
      app.UseCors(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithOrigins("http://localhost:8100"));
      
  3. 连接状态错误标记

    • 前端createHubConnection方法未等待连接成功就直接标记connectedToHub = true,导致状态与实际连接情况不符。
    • 修复:修改组件和服务代码,在连接成功回调中更新状态:
      // share-live.service.ts
      createHubConnection(username: string): Promise<void> {
        this.hubConnection = new HubConnectionBuilder()
          .withUrl(this.hubUrl + 'locationsharers',{
            skipNegotiation: true,
            transport: HttpTransportType.WebSockets
          })
          .build();
        return this.hubConnection.start()
          .then(() => this.sendNewSharer(username))
          .catch(error => {
            console.log(error);
            throw error;
          });
      }
      
      // share-live.component.ts
      private createHubConnection(): void {
        this.shareLiveService.createHubConnection(this.username)
          .then(() => this.connectedToHub = true)
          .catch(() => this.connectedToHub = false);
      }
      
  4. 缺失连接状态监听

    • 未监听SignalR连接的关闭、重连事件,无法及时排查连接异常原因。
    • 修复:在share-live.service.ts的createHubConnection中添加状态监听:
      this.hubConnection.onclose(error => {
        console.log('连接关闭:', error);
      });
      
      this.hubConnection.onreconnecting(error => {
        console.log('正在重连:', error);
      });
      
      this.hubConnection.onreconnected(connectionId => {
        console.log('重连成功:', connectionId);
      });
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 01:01:07