Client automatically close after establishing connection
I'm writing a client program to connect to a server with tls setup and listen for messages. The case is when the connection established, the OnClose callback was called after approximately 45s. When I connect to a localhost socket.io server without tls, this issue never happens.
Now I'm stuck and don't know why the problem occurs, please help.
Log:
[2021-09-08 09:03:04.939170] [0x0000000119693dc0] [info] Connecting to host
[2021-09-08 09:03:04.939805] [0x0000000119693dc0] [debug] Wait for connection to finish
[2021-09-08 09:03:05] [connect] Successful connection
[2021-09-08 09:03:05] [connect] WebSocket Connection 113.177.27.162:3335 v-2 "WebSocket++/0.8.2" /socket.io/?EIO=4&transport=websocket&t=1631066584 101
[2021-09-08 09:03:05.102168] [0x0000700007803000] [info] OnConnected --> start
[2021-09-08 09:03:05.102197] [0x0000700007803000] [info] OnConnected --> done
[2021-09-08 09:03:05.102252] [0x0000000119693dc0] [debug] Connection finished
[2021-09-08 09:03:05.102445] [0x0000000119693dc0] [debug] Namespace: /
[2021-09-08 09:03:50.143203] [0x0000700007803000] [info] sio closed: normal
Server configuration:
const redisAdapter = require('socket.io-redis');
const io = require('socket.io')(server,{
adapter: redisAdapter({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
key: process.env.REDIS_NAMESPACE
})
});
Client code:
#include <string>
#include <mutex>
#include <unistd.h>
#include "sio_client.h"
#include "boost/log/trivial.hpp"
std::string PrintSocketIOMessage(const sio::message::ptr& message);
void OnConnected();
void OnClose(sio::client::close_reason const& reason);
void OnFail();
void OnError(sio::message::ptr const& message);
void BindEvents(const sio::socket::ptr& socket);
bool connection_finish = false;
std::mutex lock;
std::unique_lock<std::mutex> unique_lock(lock);
std::condition_variable cond;
int main(int argc, char** argv) {
sio::client client;
std::string host = argv[1];
client.set_open_listener(&OnConnected);
client.set_close_listener(&OnClose);
client.set_fail_listener(&OnFail);
client.connect(host);
BOOST_LOG_TRIVIAL(info) << "Connecting to " << host;
if (!connection_finish) {
BOOST_LOG_TRIVIAL(debug) << "Wait for connection to finish";
cond.wait(unique_lock);
}
BOOST_LOG_TRIVIAL(debug) << "Connection finished";
BindEvents(client.socket());
BOOST_LOG_TRIVIAL(debug) << "Namespace: " << client.socket()->get_namespace();
while (true) {
sleep(1);
}
BOOST_LOG_TRIVIAL(debug) << "Closing";
client.sync_close();
client.clear_con_listeners();
return 0;
}
std::string PrintSocketIOMessage(const sio::message::ptr& message) {
std::string result;
switch (message->get_flag()) {
case sio::message::flag_boolean: {
result.append(message->get_bool() ? "true" : "false");
} break;
case sio::message::flag_integer: {
result.append(std::to_string(message->get_int()));
} break;
case sio::message::flag_double: {
result.append(std::to_string(message->get_double()));
} break;
case sio::message::flag_string: {
result.append(message->get_string());
} break;
case sio::message::flag_array: {
result.append("[ ");
for (auto &it : message->get_vector()) {
PrintSocketIOMessage(it);
if (&it != &message->get_vector().back()) {
result.append(", ");
}
}
result.append(" ]");
} break;
case sio::message::flag_object: {
result.append("{ ");
for (auto it = message->get_map().begin(); it != message->get_map().end(); ++it) {
result.append(it->first).append(": ").append(PrintSocketIOMessage(it->second));
if (it != std::prev(message->get_map().end())) {
result.append(", ");
}
}
result.append(" }");
} break;
case sio::message::flag_binary: {
result = "binary type";
} break;
case sio::message::flag_null: {
result = "null type";
} break;
}
return result;
}
void OnConnected() {
BOOST_LOG_TRIVIAL(info) << "OnConnected --> start";
connection_finish = true;
cond.notify_all();
BOOST_LOG_TRIVIAL(info) << "OnConnected --> done";
}
void OnClose(sio::client::close_reason const& reason) {
BOOST_LOG_TRIVIAL(info) << "sio closed: " << (reason == sio::client::close_reason::close_reason_normal ? "normal" : "drop");
exit(0);
}
void OnFail() {
BOOST_LOG_TRIVIAL(info) << "sio failed";
exit(0);
}
void OnError(sio::message::ptr const& message) {
BOOST_LOG_TRIVIAL(info) << "error: " << PrintSocketIOMessage(message);
}
void BindEvents(const sio::socket::ptr& socket) {
socket->on_error(&OnError);
socket->on("add user", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool is_ack, sio::message::list &ack_resp) {
lock.lock();
BOOST_LOG_TRIVIAL(info) << "add user: " << PrintSocketIOMessage(data);
lock.unlock();
}));
socket->on("new message", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool is_ack, sio::message::list &ack_resp) {
lock.lock();
BOOST_LOG_TRIVIAL(info) << "new message: " << PrintSocketIOMessage(data);
lock.unlock();
}));
socket->on("typing", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool is_ack, sio::message::list &ack_resp) {
lock.lock();
BOOST_LOG_TRIVIAL(info) << "typing: " << PrintSocketIOMessage(data);
lock.unlock();
}));
socket->on("stop typing", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool is_ack, sio::message::list &ack_resp) {
lock.lock();
BOOST_LOG_TRIVIAL(info) << "stop typing: " << PrintSocketIOMessage(data);
lock.unlock();
}));
}
How to fix the bug?
same issue In my case, the reason is that server does not send ping message to client and client do not pong for it. when ping_timeout reached, server send a disconnect Control frame with opcode 8 to client. and disconnection occur.
the soultion is keep socket.io server and client same version. if server(js) is version 2.x, client(cpp) version should be 2.x; if server(js) version is 3.x, client(cpp) version should be 3.x;
maybe different version has different processing for ping-pong, in socket.io-swift code we can find a if case for version.rawValue >= 3
private func handlePing(with message: String) { if version.rawValue >= 3 { write("", withType: .pong, withData: []) }
client?.engineDidReceivePing()
}