I am writing a WebSocket client application, based on the example, where the client application needs to pass the WebSocket Protocol
while establishing connection with the server. Since QtWebSockets does not support the Websocket protocols, I am writing a C++ wrapper to use libWebSockets library and emit
connected, disconnected, textReceived
kind of signals similar to QWebSocket.
My client application is able to connect to the server and is receiving the text message from the server, and I have copied minimum example code below, here I am facing an issue that when I emit a signal from the callback function the signal is not actually published and the slot I have connected to this signal never executed. I have verified that the object address passed in the session data is correct (copied the Logs of my program). What am I doing wrong here.
WebSocket.h
class WebSocket : public QObject
{
Q_OBJECT
public:
WebSocket(QObject *parent = Q_NULLPTR);
~WebSocket();
signals:
void connected();
void disconnected();
void textMessageReceived(const QString &message);
private:
static int callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
struct lws_context *context;
struct lws *client_wsi;
static const struct lws_protocols protocols[];
};
WebSocket.cpp
const struct lws_protocols WebSocket::protocols[] = {
{
"dumb_protocol",
callback_dumb_increment,
sizeof(per_session_data),
0,
},
{ NULL, NULL, 0, 0 }
};
WebSocket::WebSocket(QObject *parent) : QObject(parent)
{
struct lws_context_creation_info info;
struct lws_client_connect_info i;
int n = 0;
memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
info.protocols = protocols;
qDebug() << "[parent] address: " << this;
info.user = this;
/*
* since we know this lws context is only ever going to be used with
* one client wsis / fds / sockets at a time, let lws know it doesn't
* have to use the default allocations for fd tables up to ulimit -n.
* It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
* will use.
*/
info.fd_limit_per_thread = 1 + 1 + 1;
context = lws_create_context(&info);
if (!context) {
qDebug() << "lws init failed";
}
memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
i.context = context;
i.port = 7070;
i.address = "localhost";
i.path = "/";
i.host = i.address;
i.origin = i.address;
i.pwsi = &client_wsi;
lws_client_connect_via_info(&i);
while (n >= 0 && client_wsi)
n = lws_service(context, 0);
}
int WebSocket::callback_dumb_increment( struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len )
{
/* This will be same for every connected peer */
void *userdata = lws_context_user(lws_get_context(wsi));
qDebug() << "userData address: " << userdata;
QString message = "";
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
if (in)
qDebug() << "CLIENT_CONNECTION_ERROR: " << (char *)in;
else
qDebug() << "CLIENT_CONNECTION_ERROR: (null)";
wsi = NULL;
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
qDebug() << __func__ << " established";
emit static_cast<WebSocket*>(userdata)->connected();
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
message = QString::fromUtf8((const char *)in);
qDebug() << "RX: " << message;
emit static_cast<WebSocket*>(userdata)->textMessageReceived(message);
break;
case LWS_CALLBACK_CLIENT_CLOSED:
wsi = NULL;
emit static_cast<WebSocket*>(userdata)->disconnected();
break;
default:
break;
}
return lws_callback_http_dummy(wsi, reason, user, in, len);
}
SocketClient.h
class SocketClient : public QObject
{
Q_OBJECT
public:
explicit SocketClient(QObject *parent = Q_NULLPTR);
~SocketClient();
public slots:
void onConnected();
void onTextMessageReceived(QString message);
private:
std::shared_ptr<WebSocket> webSock = nullptr;
};
SocketClient.cpp
SocketClient::SocketClient(QObject *parent) :QObject(parent)
{
webSock = std::make_shared<WebSocket>(this);
connect(webSock .get(), &WebSocket::connected, this, &SocketClient::onConnected);
connect(webSock .get(), &WebSocket::textMessageReceived,
this, &SocketClient::onTextMessageReceived);
}
Logs:
[parent] address: WebSocket(0x1b2cae32140)
userData address: 0x1b2cae32140
WebSocket::callback_dumb_increment established
userData address: 0x1b2cae32140
RX: "Hello World"