0

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"
user12345
  • 661
  • 8
  • 34
  • You are attempting to emit signals from within the static method. I don't think it's going to work because in order to emit a Qt signal, you need to do it from inside a non-static method of the sender which should be a `QObject` subclass containing `Q_OBJECT` macro. – Dmitry Nov 30 '19 at 13:29
  • In other words, from `callback_dumb_increment` you need to invoke some non-static method of `WebSocket` which would emit the needed signal. – Dmitry Nov 30 '19 at 13:31
  • I have even tried that, but still it did not work. I have added a non-static method `void WebSocket::emitConnected() { emit connected(); }` and in the callback method I have invoked that medhod as: `static_cast(userdata)->emitConnected();` and still the same issue. – user12345 Nov 30 '19 at 18:14

0 Answers0