24

I use websocket in javascript. But the connection close after one minute.

I am wondering somethings:

1- Is not Websocket naturaly providing with Ping/Pong messages not to close the connection? I think it have to. Otherwise what is the difference between websocket and TCP connection?

2- If I have to send the ping/pong messages, how is the ping message sent? What am I need to do? Is WebSocket object provide a ping method? Or should I call a method as websocket.send("ping") ? I am use naturaly WebSocket object in javascipt.

3- Should the server respond to Ping requests with Pong? Should this be implemented separately on the server side?

Note:Sorry for my english.

Qwer Sense
  • 487
  • 1
  • 5
  • 12

4 Answers4

22

At this point in time, heartbeats are normally implemented on the server side: there's not much you can do from the client end.

However, if the server keeps killing your socket connection, and you have no control over it, it is possible for the client to send arbitrary data to the websocket on an interval:

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your grave!
  heartbeat();
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();

I strongly recommend trying to sort out what's happening on the server end, rather than trying to work around it on the client.

ouni
  • 3,233
  • 3
  • 15
  • 21
  • 4
    https://github.com/websockets/ws#how-to-detect-and-close-broken-connections – xgqfrms Jul 30 '19 at 10:54
  • 5
    This is not ping pong. There must be some conventional way to make the client side initiate ping pong, right? – Pim van der Heijden Jul 31 '19 at 13:41
  • 2
    I appreciate the altered beast ref :) – Chris Schmitz Oct 27 '20 at 21:23
  • 5
    @ChrisSchmitz I've been waiting two long years for someone to notice that, now my spirit can finally be free~ – ouni Oct 28 '20 at 14:24
  • 1
    servers generally do not and certainly should not send pings – morgwai Dec 23 '20 at 00:13
  • 1
    servers generally do not and certainly should not send pings: it is for client to decide whether he wants to maintain the connection and server should not force keeping it open on him (this way for example you could drain battery on a mobile device as constant replying to server pings would prevent it from putting radio to sleep) – morgwai Dec 24 '20 at 06:43
  • 2
    There is nothing wrong with a ping/pong initiated from the server side if you want to regularly make sure the client socket is still connected. The whole point of ping/pong is to detect a broken pipe, not to keep the connection alive. Battery and radio management are handled by OS and so out of scope here. However, as for this answer, it is a pretty crude solution that might work in some situations. 500ms is certainly overkill if the disconnect is every 60 seconds. – Phil Sep 05 '22 at 21:31
  • @Phil _"The whole point of ping/pong is [...], not to keep the connection alive"_ well, that's not what vast majority of websocket devs expect as you may figure by many similar questions here on SO... As explained in many other posts, intermediate proxies/routers tend to kill idle connections, so keep-alive **is necessary** whether you like it or not and it should **up to client** (in most cases at least) to decide how important the given connection is to him. – morgwai Sep 02 '23 at 13:44
  • @Phil For example if I have a browser window with a websocket based chat app in the background, I don't want to lose connection while I'm working in the other window. However if I'm on a mobile device and press power to put it into sleep, I usually don't want drain the battery, so the browser should stop pinging (or better ask me what to do). – morgwai Sep 02 '23 at 13:45
10

Yes, there are ping/pong frames in websockets. Here is an example using the ws module, where the server is initiating the ping request:

const http = require('http');
const ws = require('ws');

const server = http.createServer(function(req_stream_in, res_stream_out) {
  // handle regular HTTP requests here
});
const webSocketServer = new ws.Server({
  path: "/websocket",
  server: server
});

const connected_clients = new Map();

webSocketServer.on('connection', function connection(ws_client_stream) {
  // NOTE: only for demonstration, will cause collisions.  Use a UUID or some other identifier that's actually unique.
  const this_stream_id = Array.from(connected_clients.values()).length;

  // Keep track of the stream, so that we can send all of them messages.
  connected_clients.set(this_stream_id, ws_client_stream);

  // Attach event handler to mark this client as alive when pinged.
  ws_client_stream.is_alive = true;
  ws_client_stream.on('pong', () => { ws_client_stream.is_alive = true; });

  // When the stream is closed, clean up the stream reference.
  ws_client_stream.on('close', function() {
    connected_clients.delete(this_stream_id);
  });
});

setInterval(function ping() {
  Array.from(connected_clients.values()).forEach(function each(client_stream) {
    if (!client_stream.is_alive) { client_stream.terminate(); return; }
    client_stream.is_alive = false;
    client_stream.ping();
  });
}, 1000);
ouni
  • 3,233
  • 3
  • 15
  • 21
  • I am not able to access the is_alive property in websocket object? – Shaik Syed Ali Mar 04 '19 at 05:14
  • 1
    Websocket is just another Javascript object so you can add any property on it. Its just the typescript that's not recognising it for that you can either extend the Websocket class or update the typings according to your need. – Kushagra Saxena Apr 01 '19 at 10:05
  • 1
    This works but it seems that this server side will keep the server very very busy doing this. – kta Apr 16 '20 at 07:32
  • 1
    servers generally do not and certainly should not send pings: it is for client to decide whether he wants to maintain the connection and server should not force keeping it open on him (this way for example you could drain battery on a mobile device as constant replying to server pings would prevent it from putting radio to sleep) – morgwai Dec 24 '20 at 06:43
6

Mozilla documents a dedicated convention for ping/pong.

At any point after the handshake, either the client or the server can choose to send a ping to the other party. When the ping is received, the recipient must send back a pong as soon as possible. You can use this to make sure that the client is still connected, for example.

A ping or pong is just a regular frame, but it's a control frame. Pings have an opcode of 0x9, and pongs have an opcode of 0xA. When you get a ping, send back a pong with the exact same Payload Data as the ping (for pings and pongs, the max payload length is 125). You might also get a pong without ever sending a ping; ignore this if it happens.

If you have gotten more than one ping before you get the chance to send a pong, you only send one pong.

See: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets

Find more in depth discussion about ping/pong from the browser side here: Sending websocket ping/pong frame from browser

More specifically, read the Websocket RFC 6455 about ping/pong.

Pim van der Heijden
  • 6,956
  • 4
  • 16
  • 21
0

In ouni's solution, heartbeat() wasn't kicking in. It works when it's put in an open event like this:

let socket = null;

function connect_socket() {
  socket = new WebSocket(ws_url);
  socket.on("close", connect_socket); // <- rise from your grave!
  socket.on("open", heartbeat); // heartbeat when the socket is open
}

function heartbeat() {
  if (!socket) return;
  if (socket.readyState !== 1) return;
  socket.send("heartbeat");
  setTimeout(heartbeat, 500);
}

connect_socket();
synkro
  • 414
  • 7
  • 9