Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Qt Wrapper class on libWebSockets - not emitting a signal from the callback method



  • 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"
    

  • Lifetime Qt Champion

    Is the Qt eventloop running? What happens inside SocketClient::onTextMessageReceived() ? What does QtWebSockets does not support so you need to use another library?



  • @Christian-Ehrlicher I have written my SocketClient as a library in Windows, this library is instantiated inside a QMainWindow application. Initially I used the QWebSocket and I received all the signals from the QWebSocket in my main window, now that I have replaced QWebSocket class inside SocketClient with my WebSocket class, and now I am not receiving the signals in my main window.

    The reason I went with my own class is because of the reason below:

    QWebSocket currently does not support WebSocket Extensions and WebSocket Subprotocols.
    

  • Lifetime Qt Champion

    @chaithubk said in Qt Wrapper class on libWebSockets - not emitting a signal from the callback method:

    QWebSocket currently does not support WebSocket Extensions and WebSocket Subprotocols.

    What exactly do you need from those protocols?

    Did you actually add a breakpoint or some debug output inside onConnected() / onTextMessageReceived()?



  • @Christian-Ehrlicher SubProtocol is a negotiation between server and client, this is something I can't remove from the server implementation. SO, client definitely need to pass the subprotocol to connect with the server.

    Yes, I have put breakpoints and logs inside the slots, they were never executed.


  • Lifetime Qt Champion

    Maybe a threading issue? Is the callback executed in the correct thread? Please check with QThread::currentThreadId(). Otherwise I've no real idea.



  • @Christian-Ehrlicher The thread ID matched, the callback method is executed in the same thread.



  • @Christian-Ehrlicher I figured it's a threading issue, I should launch the Websocket instance in a separate thread, that resolved the problems and signals are passed as expected.


Log in to reply