Skip to content

Client: C++ client connections timeouts from both server and client sides #3701

@AdrianDC

Description

@AdrianDC

You want to:

  • report a bug
  • request a feature

Current behaviour

Following the #3698 issue, which I'll use as basis for this ticket,
I face ping timeouts from both the server and the client side (around 30 seconds).

Client side

Turns out the Socket.IO C++ client receives the pingTimeout and pingInterval upon server handshake,
and starts sending ping packages to the server, which doesn't handle incoming ping events and
the C++ drop the connection as timeout due to the missing pong responses.

However, the C++ client code is clever enough to reset the pong timeout for every incoming message event,
hence I can workaround and preserve the C++ client connection by sending custom ping events every pingTimeout / 2 ms.

Server side

Once the C++ client no longer considers the server as being dead, we observe a ping timeout on the server as well,
because the server sends ping events at pingInterval (25s) periods and times out at pingInterval + pingTimeout (30s).

I managed to keep the server connection alive by having pingInterval < pingTimeout,
for example pingInterval at 20s and pingTimeout at 30s.

Steps to reproduce (if the current behaviour is a bug)

File test.js :

"use strict";

// Libraries
const express = require("express");
const http = require("http");
const socketio = require("socket.io");

// Variables
const port = 3000;
const app = express();
const serverHTTP = http.Server(app);

// Server
console.log("[SERVER] Creating Socket.IO@3.0.X Server");
const io = new socketio.Server(serverHTTP, {
  pingTimeout: 500, // Default: 5000
  pingInterval: 2500, // Default: 25000
});

// Namespace
io.of("/user/").use((socket, next) => {
  const data = JSON.stringify(socket.handshake.query);
  console.log('[SERVER] Connected ("/user/".use)', data);
  socket.on("disconnect", (data) => {
    console.log('[SERVER] Disconnect ("/user/".use)', data);
  });
  next();
});

// Listen
serverHTTP.listen(port, () => {
  console.log(`[SERVER] Server listening on http://127.0.0.1:${port}`);
});

File test.cpp : test.cpp

Compile socket.io-client-cpp and test :

git clone --recurse-submodules https://github.com/socketio/socket.io-client-cpp.git
docker run -i --entrypoint bash --rm -v "$PWD:$PWD" -w "$PWD" node:12-stretch <<EOF
cd ./socket.io-client-cpp/
apt update
apt install -y libboost-all-dev build-essential cmake libssl1.0-dev make
cmake .
make install
cd ../
g++ -std=c++11 -Isocket.io-client-cpp/build/include -lboost_system -lpthread -o test test.cpp ./socket.io-client-cpp/build/lib/Release/libsioclient.a
EOF

Run with socket.io@3.0.2 :

docker run -i --entrypoint bash --rm -v "$PWD:$PWD" -w "$PWD" node:12-stretch <<EOF
apt update
apt install libboost-system1.62.0
npm install express http socket.io@3.0.2
echo ''
{
  node ./test.js &
  server=${!}
  sleep 2
  timeout 30 ./test true
  kill -9 "${server}"
}
EOF

Results with socket.io@3.0.2 :

[SERVER] Creating Socket.IO@3.0.X Server
[SERVER] Server listening on http://127.0.0.1:3000
[CLIENT] Connecting to http://127.0.0.1:3000...
[2020-11-21 23:15:09] [connect] Successful connection
[CLIENT] Connected
[SERVER] Connected ("/user/".use) {"EIO":"4","transport":"websocket","t":"1606000509","query1":"data1","query2":"data2"}
[2020-11-21 23:15:12] [disconnect] Disconnect close local:[1008,Pong timeout] remote:[1006]
[SERVER] Disconnect ("/user/".use) ping timeout
[2020-11-21 23:15:17] [connect] Successful connection
[CLIENT] Connected
[SERVER] Connected ("/user/".use) {"EIO":"4","transport":"websocket","t":"1606000517","query1":"data1","query2":"data2"}
[2020-11-21 23:15:20] [disconnect] Disconnect close local:[1008,Pong timeout] remote:[1006]
[SERVER] Disconnect ("/user/".use) transport close
[2020-11-21 23:15:25] [connect] Successful connection
[CLIENT] Connected
[SERVER] Connected ("/user/".use) {"EIO":"4","transport":"websocket","t":"1606000525","query1":"data1","query2":"data2"}
[2020-11-21 23:15:28] [disconnect] Disconnect close local:[1008,Pong timeout] remote:[1006]
[SERVER] Disconnect ("/user/".use) ping timeout
[2020-11-21 23:15:33] [connect] Successful connection
[CLIENT] Connected
[SERVER] Connected ("/user/".use) {"EIO":"4","transport":"websocket","t":"1606000533","query1":"data1","query2":"data2"}
[2020-11-21 23:15:36] [disconnect] Disconnect close local:[1008,Pong timeout] remote:[1006]
[SERVER] Disconnect ("/user/".use) ping timeout
[CLIENT] Interrupting...
[CLIENT] State: Disconnected

Expected behaviour

Both detailed behaviours are workarounds for issues that shouldn't happen.
Socket.IO was migrated from 2.3.0 to 3.0.2 and the C++ client remains unchanged.

I resolved support on both ends of the connection with the following changes :

 // Server
 console.log("[SERVER] Creating Socket.IO@3.0.X Server");
 const io = new socketio.Server(serverHTTP, {
-  pingTimeout: 500, // Default: 5000
+  pingTimeout: 3000, // Default: 5000
   pingInterval: 2500, // Default: 25000
 });
 
 // Namespace
 io.of("/user/").use((socket, next) => {
+  const ping = setInterval(() => {
+    socket.emit("ping", {});
+  }, io.eio.opts.pingTimeout / 2);
   const data = JSON.stringify(socket.handshake.query);
   console.log('[SERVER] Connected ("/user/".use)', data);
   socket.on("disconnect", (data) => {
+    if (ping) {
+      clearInterval(ping);
+    }
     console.log('[SERVER] Disconnect ("/user/".use)', data);
   });
[SERVER] Creating Socket.IO@3.0.X Server
[SERVER] Server listening on http://127.0.0.1:3000
[CLIENT] Connecting to http://127.0.0.1:3000...
[2020-11-21 23:21:57] [connect] Successful connection
[CLIENT] Connected
[SERVER] Connected ("/user/".use) {"EIO":"4","transport":"websocket","t":"1606000917","query1":"data1","query2":"data2"}
[CLIENT] Interrupting...
[CLIENT] State: Connected
[CLIENT] Disconnected
[2020-11-21 23:22:27] [disconnect] Disconnect close local:[1000,End by user] remote:[1000,End by user]
[SERVER] Disconnect ("/user/".use) transport close

Setup

  • OS: Debian Stretch and CentOS 7
  • Node.js : 12.19.1
  • socket.io version: 3.0.2

Other information (e.g. stacktraces, related issues, suggestions how to fix)

For CentOS 7 targets, compatibility for socket.io-client-cpp : socketio/socket.io-client-cpp#271

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions