Angular4 Websocket rxjs Reconnect и onError

Похоже, есть несколько похожих вопросов, но через несколько дней я не нахожу правильного ответа. Мой вопрос в том, как узнать, закрыл ли сервер веб-узел, и как попытаться восстановить соединение. Я видел несколько примеров, но ни один из них не работал должным образом, когда я хотел реализовать функциональность закрытия веб-сокета от клиента при изменении представления.

Затем я нашел этот пример, лучший из тех, что я видел до сих пор, и с небольшими изменениями я смог добавить функцию закрытия, которая работает довольно хорошо.

Закрытие веб-сокета от клиента больше не проблема, однако я не могу знать, когда веб-сокет закрыт сервером и как снова переподключить.

Мой код очень похож на вопрос это, но я прошу другое. Кроме того, у меня были проблемы с повторным использованием веб-сокета, пока я не увидел функцию общего доступа, о которой они говорят в указанной мной ссылке, поэтому в опубликованном мною классе служба веб-сокета и служба, которая использовала службу веб-сокета, объединены в один

Моя служба веб-сокетов

import { Injectable } from '@angular/core';
import { Observable, Observer, Subscription } from 'rxjs';
import { Subject } from 'rxjs/Subject';

import { ConfigService } from "../config.service";

@Injectable()
export class WSEtatMachineService {
    public messages: Subject<any>  = new Subject<any>();
    private url: string = '';
    static readonly ID = 'machine';

    private _subject: Subject<MessageEvent>;
    private _subjectData: Subject<number>;
    private _ws: any;

    constructor(private configService: ConfigService) {
        console.info('constructyor ws machine service')
        this.setUrl(WSEtatMachineService.ID)
    }

    public setUrl(id:string) {
        const host = this.configService.getConfigReseau().ipServer;
        const port = this.configService.getConfigReseau().portServer;
        this.url = `ws://${host}:${port}/` + id 
    }

    public connect() {
        console.info('connect ws machine service ', this.url)
        this.messages = <Subject<any>>this._connect(this.url)
            .map((response: any): any => {
                console.info('ws etat machine service: ', response)
                return JSON.parse(response.data);
            })

    }

    public close() {
        console.info('on closing WS');
        this._ws.close()
        this._subject = null
    }

    public send(msg: any) {
        this.messages.next(JSON.stringify(msg));
    }

    // Private methods to create the websocket

    public _connect(url: string): Subject<MessageEvent> {
        if (!this._subject) {
            this._subject = this._create(url);
        }
        return this._subject;
    }

    private _create(url: string): Subject<MessageEvent> {
        this._ws = new WebSocket(url);

        let observable = Observable.create(
            (obs: Observer<MessageEvent>) => {
                this._ws.onmessage = obs.next.bind(obs);
                this._ws.onerror   = obs.error.bind(obs);
                this._ws.onclose   = obs.complete.bind(obs);
                return this._ws.close.bind(this._ws);
            }).share();

        let observer = {
            next: (data: Object) => {
                if (this._ws.readyState === WebSocket.OPEN) {
                    this._ws.send(JSON.stringify(data));
                }
            }
        };

        return Subject.create(observer, observable);
    }
} // end class 

Затем в компоненте я делаю:

constructor( private wsMachineService: WSMachineService) { ... }

ngOnInit() { 
...
this.wsMachineService.connect();
    // connexion du web socket
    this.wsMachineService.messages.subscribe(
      machine => {
        console.info(" wsMachineService open and alive", machine);

      },
      err => {
        // This code is never executed
        console.info(" wsMachineService closed by server!!", err);
      }
    );

}

ngOnDestroy() {
    //let tmp = this.synoptiqueSocketSubscription.unsubscribe();
    this.wsMachineService.messages.unsubscribe();
    this.wsMachineService.close()
}

Думаю, мне что-то не хватает в функции _create, потому что я пытаюсь уловить тему функции подключения, но она не работает.

Есть идеи, как я могу узнать, закрывается ли он, и попытаться снова подключиться?

Спасибо


Редактировать: Я думаю, что моя проблема связана с предметом / наблюдаемыми, поскольку я не контролирую их полностью. У меня был старый подход, когда я мог знать, когда сервер был мертв, и он пытался повторно подключиться каждые X секунд, но, к сожалению, я не смог закрыть соединение от клиента, так как у меня не было доступа к объекту websocket. Я тоже добавляю код:

  public messages: Observable<any>;
  private ws: Subject<any>;
  private url: string;
  public onclose = new Subject();

  public connect(urlApiWebSocket: string): Observable<any> {
    if (this.messages && this.url === urlApiWebSocket) {
      return this.messages;
    }
    this.url = urlApiWebSocket;
    this.ws = Observable.webSocket({
      url: urlApiWebSocket,
      closeObserver: this.onclose
    });
    return this.messages = this.ws.retryWhen(errors => errors.delay(10000)).map(msg => msg).share();
  }

  send(msg: any) {
    this.ws.next(JSON.stringify(msg));
  }

Посмотрим, сможем ли мы объединить оба решения.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
0
2 681
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

У вас нет никакого способа узнать, когда сервер закрывает соединение с клиентом.

Как вы уже заметили, this._ws.onclose = obs.complete.bind(obs); сработает только тогда, когда клиент выполнит действие «закрыть».

Обычный способ поиграть:

  • onClose вашего сервера, вы отправляете специальное сообщение всем своим клиентам, чтобы уведомить об этом.
  • Создайте механику пинга, чтобы спросить у сервера, жив ли он.

Привет, спасибо, что ответили. Раньше у меня был код вроде этого this.ws = Observable.webSocket({ url: myUrl, closeObserver: this.onclose }); return this.messages = this.ws.retryWhen(errors => errors.delay(10000)).map(msg => msg).share();, и я мог знать, когда сервер был мертв, и когда ws пытался повторно подключиться, пока он не стал доступным. Однако при таком подходе я не мог получить доступ к самому ws (он использует наблюдаемый веб-сокет), поэтому я не мог закрыть его от клиента. Я думаю, что моя проблема связана с наблюдаемым и предметом, но я этого не вижу.

Javier 14.03.2018 17:45

В вашем примере вы не можете сделать this.ws.unsubscribe (); заставить Observable закрыть соединение от клиента?

Yanis-git 14.03.2018 19:35

Если вы говорите о первом подходе, который я использовал, когда я отказывался от подписки, иногда он закрывал соединение, а иногда нет. С новым делаю закрытие работает всегда

Javier 15.03.2018 08:41
Ответ принят как подходящий

Что ж, я нашел способ. Я использую старый подход с Observable.websocket

@Injectable()
export class WSMyService {
    private url: string = '';
    static readonly ID = 'mytestwebsocket';
    readonly reload_time = 3000;

    public messages: Observable<any>;
    private ws: Subject<any>;
    public onclose = new Subject();
    constructor(private configService: ConfigService) {
        console.info('constructor ws synop service')
        this.setUrl(WSActionFrontService.ID)
    }

    public setUrl(id:string) {
        ...
        this.url = `ws://${host}:${port}/` + id 
    }

    public connect(): Observable<any> {
      this.ws = Observable.webSocket({
        url: this.url,
        closeObserver: this.onclose
      });
      return this.messages = this.ws.retryWhen(errors => errors.delay(this.reload_time)).map(msg => msg).share();
    }

    send(msg: any) {
      this.ws.next(JSON.stringify(msg));
    }

    public close() {
        console.info('on closing WS');
        this.ws.complete();
    }

и когда я его использую:

constructor(
    private wsMyService: WSMyService,

ngOnInit():
  ...
  this.mySocketSubscription = this.wsMyService.connect().subscribe(message => {
 ... }
  this.wsMyService.onclose.subscribe(evt => {
  // the server has closed the connection
  })

ngOnDestroy() {

    this.wsMyService.close();
    this.mySocketSubscription.unsubscribe();
    ...
}

Похоже, все, что мне нужно было сделать, это вызвать функцию complete (), которая сообщает серверу, что клиент завершил работу. Я уверен, что есть способ получше, но это единственный способ, который мне подходит.

Спасибо за помощь.

Другие вопросы по теме