ReactiveX / rxjs

A reactive programming library for JavaScript
https://rxjs.dev
Apache License 2.0
30.81k stars 3.01k forks source link

WebSocketSubject: Keep-Alive and messages queue #5175

Open aranz00 opened 4 years ago

aranz00 commented 4 years ago

Hi mates,

i implemented keep-alive with ping pong pattern for check the connection state (because in some situation websocket believes to be connected but it really is not (for example by disconnecting the network cable). When client websocket send a ping to server, the server send a pong so client can know if the trasport channel is really up. In the event that the client does not notice that it is disconnected instead it send a ping and no pong receive (because server is unreachable) safter a while the client tries to postpone another ping and retry so on... so many ping are queued in WebSocketSubject queue (destination is a ReplaySubject unlimited). So when server come up, client send all ping queued :(

Is there a way not to queue the pings in the queue? I see destination parameter in WebSocketSubject ctor but i don't know ad is works. Can you help me? Can i send a message without appending it in the queue?

Thank you

Version: "rxjs": "^6.5.3",

aranz00 commented 4 years ago

At the moment i have try this workaround for ping message:

const tmpSrv = (that.server as any);
const tmpWs = tmpSrv._socket as WebSocket;
if (tmpWs != null) {
    const serializer = tmpSrv._config.serializer;
    tmpWs.send(serializer(obj));
}

where server is websocketsubject. But i have the same issues. Have you another solution? Why websocket buffering all request?

Cammeritz commented 4 years ago

Is there a way not to queue the pings in the queue?

Why don't you just check if you received a "pong" message on the client side before sending another "ping" packet?

aranz00 commented 4 years ago

Is there a way not to queue the pings in the queue?

Why don't you just check if you received a "pong" message on the client side before sending another "ping" packet?

Hi @Cammeritz I'm doing it. Every message has a timeout (on client side) so if i send a ping and server is in a limbo state (es unlinked ethernet) ping request will go in timeout state (but packet will be queued on weboscket queue). After timeout i retry 4 times to call server from client with other 4 ping packet (all their with timeout logic). I'm waiting for pong respose before send another ping, but when server is in a limbo state all ping request reach timeout time. So after receive timeout response i send another ping ... but all this ping will be queued on websocket queue.

I have found only one solution. Close socket and reopen it. The issue was because this library when you call .complete or .close mantain callback options inside (beacuse the librari websocketsubject create a reference copy to external object). So when close websocket in a limbo state it doesn't close immediately but after some times, meantime the new socket going up. When the first socket is finally closed it's call close callback going to interfere with the callback of the new socket. My solution for this was set config options to empty object before close it.

(this.server as any)._config = {};
this.server.unsubscribe();
this.server = null;

server is my websocketsubject. In this mode when old socket will be close i won't have close callback interference

Cammeritz commented 4 years ago

Have you tried adding a WebSocketSubjectConfig? When you provide one you can define an closeObserver which should trigger if the websocket disconnects

https://github.com/ReactiveX/rxjs/blob/b90b397b0ab3250ac56dd22de5e2d4ed69fdf57f/src/internal/observable/dom/WebSocketSubject.ts#L124

https://github.com/ReactiveX/rxjs/blob/b90b397b0ab3250ac56dd22de5e2d4ed69fdf57f/src/internal/observable/dom/WebSocketSubject.ts#L334

aranz00 commented 4 years ago

Have you tried adding a WebSocketSubjectConfig? When you provide one you can define an closeObserver which should trigger if the websocket disconnects

https://github.com/ReactiveX/rxjs/blob/b90b397b0ab3250ac56dd22de5e2d4ed69fdf57f/src/internal/observable/dom/WebSocketSubject.ts#L124

https://github.com/ReactiveX/rxjs/blob/b90b397b0ab3250ac56dd22de5e2d4ed69fdf57f/src/internal/observable/dom/WebSocketSubject.ts#L334

Oh yes, they are the "callback options" in my previous message.

Cammeritz commented 4 years ago

Another idea i have would be to check it the bufferedAmount in the WebSocket is 0. If its greater than 0 do not send a ping packet. https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/bufferedAmount

The WebSocket.bufferedAmount read-only property returns the number of bytes of data that have been queued using calls to send() but not yet transmitted to the network. This value resets to zero once all queued data has been sent. This value does not reset to zero when the connection is closed; if you keep calling send(), this will continue to climb.

aranz00 commented 4 years ago

Thank you @Cammeritz but i can't use this solution because in queue i could have other client request (ex. get all items from backend). So in queue i can have ping packet but data packet too. When server going available all queue message are processed and queued data packet will be managed from server. So bufferedAmount could be greater than 0 but for other packet request and not for ping packet.

Cammeritz commented 4 years ago

So bufferedAmount could be greater than 0 but for other packet request and not for ping packet.

@aranz00 Ah yeah thats true.

Why don't you try it the other way around: Server pings, client pongs. If the client receives no ping-packets you know that the connection is closed/broken. The server also does not send any ping-packet before he received a pong-packet.

aranz00 commented 4 years ago

@Cammeritz Thank you, client pong is native in browser websocket implementation so pong is automatically send to server after server ping. So my Angular APP not know if a ping is receved. Server use ping opcode of the websocket protocol (i'm using Websocketpp C++). I'm using opcode beacuse its have max response proprity and itn't insert into messages queue (server side). Client side browser don't get access to opcode frame of the websocket protocol so i have implementend standard message with text opcode.

maxchene commented 1 month ago

@aranz00 Would you mind sharing your final solution with opcode ping pong using rxjs ? I'm in the same situation and could'nt figure it out