Comunication between java/spring and c++/qt applications with websockets

Viewed 443

I am trying implement websockets using spring with a java/web application to allow it to exchangte messages with a application written with c++ using qt (and the websockets library from it).

I have in my java/spring application this configuration:

WebScoketConfig.java

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new SocketHandler(), "/name");
    }
}

SocketHandler.java

@Component
public class SocketHandler extends TextWebSocketHandler {
    List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException {
        Map<String, String> value = new Gson().fromJson(message.getPayload(), Map.class);
        session.sendMessage(new TextMessage("Hello " + value.get("name") + " !"));
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
    }
}

and I created a very simple qt-creator project, with a main function and one class MainWindow, with two objects: a lineEdit, where the user types a message to send to server, and a pushButton, to proceed with the data send.

In my MainWindow class, I implement this slot to handle the data exchange:

void MainWindow::on_pushButton_clicked()
{
    QString message = this->ui->lineEdit->text();
    QWebSocket m_webSocket;
    m_webSocket.open(QUrl(QStringLiteral("ws://localhost:8080/name")));
    m_webSocket.sendTextMessage("Hello " + message + " !");
    m_webSocket.close();
}

But when I execute both applications, and try send a message for the java/web application, nothing happens. I pretty sure the mistake I made it's on the c++/qt side, since in the java/spring side I have a html/javascript code which allow me testing the message exchage, nd works fine.

Anyone can tell me what I am doing wrong here?

update: minimal reproducible example - java/spring

the project can be generated with start.spring.io, only with spring-websocket as dependency. besides the 2 files I already add above, the project will have:

resources/static/index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello WebSocket</title>
  <link href="/main.css" rel="stylesheet">
</head>
<body>
  <table>
    <tr>
      <td>
        <button id="connect" type="button" onclick="connect();">Connect</button>
        <button id="disconnect" type="button" disabled="disabled" onclick="disconnect();">Disconnect</button>
      </td>
      <td>
        <label for="name">What is your name?</label>
        <input type="text" id="name" placeholder="Your name here...">
        <button id="send" type="button" onclick="send();">Send</button>
      </td>
    </tr>
  </table>

  <hr>

  <table id="conversation" border="2">
      <thead>
      <tr>
          <th>Greetings</th>
      </tr>
      </thead>
      <tbody id="greetings">
      </tbody>
  </table>

  <script src="/app.js"></script>
</body>
</html>

resources/app.js

var ws;

function connect() {
    ws = new WebSocket('ws://localhost:8080/name');

    ws.onmessage = function(text) {
    var tr = document.createElement("tr");
    var td = document.createElement("td");
    td.innerText = text.data;
    tr.appendChild(td);
    document.querySelector("#greetings").appendChild(tr);
    }

  document.querySelector("#connect").setAttribute("disabled", "disabled");
  document.querySelector("#disconnect").removeAttribute("disabled");
  document.querySelector("#conversation").style.display = 'block';
  document.querySelector("#greetings").innerHTML = "";
}

function disconnect() {
    if (ws != null)
        ws.close();

    document.querySelector("#connect").removeAttribute("disabled");
    document.querySelector("#disconnect").setAttribute("disabled", "disabled");
    document.querySelector("#conversation").style.display = 'none';
    document.querySelector("#greetings").innerHTML = "";
}

function send() {
  var name = document.querySelector("#name");
    var data = JSON.stringify({'name': name.value});
  ws.send(data);
}

after build with mvn package, just run with java -jar target/app.jar.

update: minimal reproducible example - c++/qt

project is created with qt-creator, as type qt-widget. It will create a project with 5 files: websocket.pro, mainwindow.ui, mainwindow.h, mainwindow.cpp and main.cpp.

Open mainwindow.ui and add a lineEdit and pushButton from toolbar. right-click on the pushButton and choose Go to slot and select clicked(). Add the code above.

Update 2

void MainWindow::on_pushButton_clicked()
{
    QString message = ui->lineEdit->text();

    connect(&m_webSocket, &QWebSocket::connected, [this, message](){
        QJsonObject object
        {
            {"name", message}
        };
        QJsonDocument d(object);
        m_webSocket.sendTextMessage(d.toJson().toStdString().c_str());
        m_webSocket.close();
    });

    m_webSocket.open(QUrl(QStringLiteral("ws://localhost:8080/name")));
}
1 Answers

The problem is that you are trying to send the text without verifying that the connection is successful. The solution is to use the connected signal, in addition to making m_webSocket a member of the class as advised in the comments:

*.h

private:
    Ui::MainWindow *ui;
    QWebSocket m_webSocket;

*.cpp

void MainWindow::on_pushButton_clicked()
{
    QString message = ui->lineEdit->text();

    connect(&m_webSocket, &QWebSocket::connected, [this, message](){
        m_webSocket.sendTextMessage("Hello " + message + " !");
        m_webSocket.close();
    });

    m_webSocket.open(QUrl(QStringLiteral("ws://localhost:8080/name")));
}

Update:

In your project I have noticed the following errors:

  • For some reason when I tested using Google Chrome I could not connect so I added registry.addHandler(new SocketHandler(), "/name").setAllowedOrigins("*"); to the configuration.

  • The variable "session" only handles sending data to a socket, if you want to send that information to all sockets (js and qt) then you must iterate.

  • When a session is disconnected do not remove it from "sessions" which can cause errors. You must remove session in the afterConnectionClosed method.

  • In your code you are calling to connect to the server in the slot associated with the connected signal which is silly since that slot is called after the connection and for this you should first call the open method. Anyway opening the connection, waiting for the connection to be established, sending the message and closing the connection is not a good idea, it is better to open the connection before sending the message and close it when necessary (for example when closing the GUI or the user wants to close it as it does in js since the sending of information is not instantaneous but is asynchronous).

The complete code is here.

Related