Search code examples
c++qtwebsocket

Failed to retrieve data with Qt from deployed Node.js server using WebSockets


I wrote a simple server example in JavaScript that sends some data to a client when the client connects: send-gravity-from-server-to-client-box2d-wasm-js

I deployed this simple server on free hostings: https://glitch.com/ and https://render.com/. Web clients work well:

(It takes 15-20 seconds for Glitch and 50 seconds for Render to wake up the server)

I try to connect to the server from Qt and show received data:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    connect(&m_webSocket, &QWebSocket::connected, this, &Widget::onConnected);
    connect(&m_webSocket, &QWebSocket::textMessageReceived,
            this, &Widget::onMessageReceived);
    connect(&m_webSocket, &QWebSocket::errorOccurred,
            this, &Widget::errorOccurred);

    // QUrl url("wss://send-gravity-box2d-wasm-js.onrender.com");
    QUrl url("wss://merciful-regal-soursop.glitch.me");
    m_webSocket.open(url);
}

Widget::~Widget() {}

void Widget::onConnected()
{
    qDebug() << "connected";
}

void Widget::onMessageReceived(const QString &message)
{
    qDebug() << message;
}

void Widget::errorOccurred(QAbstractSocket::SocketError error)
{
    qDebug() << error;
}

I see:

connected
QAbstractSocket::RemoteHostClosedError
QAbstractSocket::RemoteHostClosedError

in the Qt Debug console when I try to connect to Render and I see QAbstractSocket::ConnectionRefusedError when I try to connect to Glitch.


Solution

  • To solve this problem on Glitch (I haven't tested it on Render yet), you need to find out the User-Agent in the web client like this:

    console.log(window.navigator.userAgent);
    

    Copy and paste the resulting string value above into the second argument of the setRawHeader() method:

        QUrl url("wss://merciful-regal-soursop.glitch.me");
    
        QNetworkRequest request;
        request.setUrl(url);
        request.setRawHeader(QByteArray("User-Agent"), QByteArray("Mozilla/5.0 "
            "(Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/124.0.0.0 Safari/537.36"));
    
        m_webSocket.open(request);
    

    The solution to the problem on Glitch was possible thanks to wh0 who helped me here:

    do you know if this sends a User-Agent header? Glitch requires one to be present. my searching shows that QWebSocket has an open method that takes a network request QWebSocket Class | Qt WebSockets 6.7.1. try using that

    My following Qt 6.6.3 example works on Android, Desktop, and Web (with Qt WebAssembly). It prints data received from the server. The server contains the Box2D-WASM library. It sends the gravity value in JSON format when a client is connected. It is useful example to make multiplayer games with physics on the server side. I have deployed the example on free Glitch hosting: https://glitch.com/edit/#!/merciful-regal-soursop from the GitHub repository: send-gravity-from-server-to-client-box2d-wasm-js The client contains only one main.cpp file. It outputs the following information to the console:

    connected
    "{\"action\":\"scGravity\",\"data\":\"{\\\"x\\\":0,\\\"y\\\":-3}\"}"
    

    You should download OpenSSL to run the following example on Android. Open the following window in Qt Creator (Edit > Preferences... > Devices > Android):

    enter image description here

    Add the following path to your pro-file:

    QT += core gui websockets widgets
    
    android: include(C:/Qt/Tools/OpenSSL-1.1.1j/Win_x64/bin/openssl.pri)
    
    CONFIG += c++17
    
    SOURCES += \
        main.cpp
    

    Read how to add OpenSSL to your CMake project if you use CMake instead of QMake in the Qt documentaion: Adding OpenSSL Support for Android

    Build the following example for Android, Desktop, and WebAssembly (I have tested it):

    main.cpp

    #include <QtNetwork/QNetworkRequest>
    #include <QtWebSockets/QWebSocket>
    #include <QtWidgets/QApplication>
    #include <QtWidgets/QWidget>
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
    
        Widget()
        {
            setWindowTitle("Show gravity from server with Box2D-WASM");
            resize(420, 200);
    
            connect(&m_webSocket, &QWebSocket::connected,
                this, &Widget::onConnected);
            connect(&m_webSocket, &QWebSocket::textMessageReceived,
                this, &Widget::onMessageReceived);
            connect(&m_webSocket, &QWebSocket::errorOccurred,
                this, &Widget::onErrorOccurred);
    
            QUrl url("wss://merciful-regal-soursop.glitch.me");
    
            QNetworkRequest request;
            request.setUrl(url);
            request.setRawHeader(QByteArray("User-Agent"), QByteArray("Mozilla/5.0 "
                "(Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/124.0.0.0 Safari/537.36"));
    
            m_webSocket.open(request);
        }
        ~Widget() {}
    
    private slots:
    
        void onConnected()
        {
            qDebug() << "connected";
        }
        void onMessageReceived(const QString &message)
        {
            qDebug() << message;
        }
    
        void onErrorOccurred(QAbstractSocket::SocketError error)
        {
            qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
            qDebug() << "Error:" << error;
            qDebug() << "Device supports OpenSSL:" << QSslSocket::supportsSsl();
            qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
        }
    
    private:
    
        QWebSocket m_webSocket;
    };
    
    #include "main.moc"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        Widget w;
        w.show();
        return app.exec();
    }