Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

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

    General and Desktop
    2
    8
    532
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • chaithubk
      chaithubk last edited by chaithubk

      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"
      
      1 Reply Last reply Reply Quote 0
      • Christian Ehrlicher
        Christian Ehrlicher Lifetime Qt Champion last edited by Christian Ehrlicher

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

        Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
        Visit the Qt Academy at https://academy.qt.io/catalog

        chaithubk 1 Reply Last reply Reply Quote 2
        • chaithubk
          chaithubk @Christian Ehrlicher last edited by

          @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.
          
          1 Reply Last reply Reply Quote 0
          • Christian Ehrlicher
            Christian Ehrlicher Lifetime Qt Champion last edited by

            @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()?

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            chaithubk 1 Reply Last reply Reply Quote 1
            • chaithubk
              chaithubk @Christian Ehrlicher last edited by

              @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.

              1 Reply Last reply Reply Quote 0
              • Christian Ehrlicher
                Christian Ehrlicher Lifetime Qt Champion last edited by

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

                Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                Visit the Qt Academy at https://academy.qt.io/catalog

                chaithubk 2 Replies Last reply Reply Quote 0
                • chaithubk
                  chaithubk @Christian Ehrlicher last edited by

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

                  1 Reply Last reply Reply Quote 0
                  • chaithubk
                    chaithubk @Christian Ehrlicher last edited by

                    @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.

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post