Komunikasi antara aplikasi Java/spring dan c++/qt dengan websockets

Saya mencoba mengimplementasikan websockets menggunakan spring dengan aplikasi Java/web untuk memungkinkannya bertukar pesan dengan aplikasi yang ditulis dengan c++ menggunakan qt (dan perpustakaan websockets darinya).

Saya memiliki konfigurasi ini di aplikasi Java/spring saya:

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);
    }
}

dan saya membuat proyek pembuat qt yang sangat sederhana, dengan fungsi main dan satu kelas MainWindow, dengan dua objek: lineEdit, tempat pengguna mengetik pesan untuk dikirim ke server, dan pushButton, untuk melanjutkan pengiriman data.

Di kelas MainWindow saya, saya menerapkan slot ini untuk menangani pertukaran data:

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();
}

Namun ketika saya menjalankan kedua aplikasi tersebut, dan mencoba mengirim pesan untuk aplikasi java/web, tidak terjadi apa-apa. Saya cukup yakin kesalahan yang saya buat ada di sisi c++/qt, karena di sisi java/spring saya memiliki kode html/javascript yang memungkinkan saya menguji pertukaran pesan, dan berfungsi dengan baik.

Adakah yang bisa memberi tahu saya apa yang saya lakukan salah di sini?

pembaruan: contoh minimal yang dapat direproduksi - Java/spring

proyek dapat dibuat dengan start.spring.io, hanya dengan spring-websocket sebagai ketergantungan. selain 2 file yang sudah saya tambahkan di atas, proyek ini akan memiliki:

resources/static/index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Hello WebSocket</title>
  <link href="/id/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);
}

setelah dibangun dengan mvn package, jalankan saja dengan java -jar target/app.jar.

pembaruan: contoh minimal yang dapat direproduksi - c++/qt

proyek dibuat dengan qt-creator, sebagai tipe qt-widget. Ini akan membuat proyek dengan 5 file: websocket.pro, mainwindow.ui, mainwindow.h, mainwindow.cpp dan main.cpp.

Buka mainwindow.ui dan tambahkan lineEdit dan pushButton dari toolbar. klik kanan pada tombol tekan dan pilih Go to slot dan pilih clicked(). Tambahkan kode di atas.

Pembaruan 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")));
}

person Kleber Mota    schedule 09.04.2020    source sumber
comment
Tidak yakin tapi... tebakan saya adalah QWebSocket::sendTextMessage tidak langsung mengirim pesan. Melainkan menunda pengiriman sampai Anda mengembalikan kontrol ke loop peristiwa Qt. Oleh karena itu, pesannya mungkin hilang ketika QWebSocket Anda berada di luar jangkauan.   -  person G.M.    schedule 09.04.2020
comment
Bagaimana saya bisa memperbaikinya?   -  person Kleber Mota    schedule 10.04.2020
comment
Sulit untuk menjawab tanpa contoh minimal yang dapat direproduksi tetapi, coba jadikan QWebSocket m_webSocket sebagai anggota data kelas MainWindow.   -  person G.M.    schedule 10.04.2020
comment
Tidak tahu apakah akan membantu apa pun, saya mencoba memperbarui pertanyaan saya dengan memasukkan contoh minimal yang dapat direproduksi ini, karena hanya menjadikan m_websocket anggota data MainWindow tidak akan membuat ini berfungsi.   -  person Kleber Mota    schedule 10.04.2020
comment
@KleberMota Anda bilang tidak terjadi apa-apa, apa maksudnya? Apa yang ingin Anda dapatkan?   -  person eyllanesc    schedule 19.04.2020
comment
ketika saya mengirim pesan di aplikasi c++/qt, seharusnya ditampilkan di aplikasi java/spring, persis seperti jika saya menggunakan antarmuka web yang tersedia di proyek java/spring.   -  person Kleber Mota    schedule 19.04.2020
comment
kesalahan apa yang kamu temukan? dalam kode c++ atau Java? Di sini, keduanya dikompilasi tanpa masalah.   -  person Kleber Mota    schedule 19.04.2020


Jawaban (1)


Masalahnya adalah Anda mencoba mengirim teks tanpa memverifikasi bahwa koneksi berhasil. Solusinya adalah dengan menggunakan sinyal terhubung, selain menjadikan m_webSocket anggota kelas seperti yang disarankan di komentar:

*.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")));
}

Memperbarui:

Dalam proyek Anda, saya melihat kesalahan berikut:

  • Entah mengapa ketika saya tes menggunakan Google Chrome saya tidak bisa terhubung jadi saya menambahkan registry.addHandler(new SocketHandler(), "/name").setAllowedOrigins("*"); pada konfigurasinya.

  • Variabel "session" hanya menangani pengiriman data ke suatu soket, jika ingin mengirimkan informasi tersebut ke semua soket (js dan qt) maka harus melakukan iterasi.

  • Ketika suatu sesi terputus, jangan menghapusnya dari "sesi" yang dapat menyebabkan kesalahan. Anda harus menghapus sesi dalam metode afterConnectionClosed.

  • Dalam kode Anda, Anda memanggil untuk terhubung ke server di slot yang terkait dengan sinyal terhubung yang konyol karena slot itu dipanggil setelah koneksi dan untuk ini Anda harus memanggil metode terbuka terlebih dahulu. Pokoknya membuka koneksi, menunggu koneksi terjalin, mengirim pesan dan menutup koneksi bukanlah ide yang baik, lebih baik membuka koneksi sebelum mengirim pesan dan menutupnya bila diperlukan (misalnya saat menutup GUI atau pengguna ingin menutupnya seperti di js karena pengiriman informasi tidak seketika tetapi tidak sinkron).

Kode lengkapnya ada di sini.

person eyllanesc    schedule 10.04.2020
comment
Menariknya, ketika saya menyalin/menempelkan kode Anda, aplikasi c++/qt sepertinya menjangkau aplikasi java/spring, tetapi saya mendapatkan kesalahan ini: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $. Jika saya mengubah kode ke yang ditambahkan ke pertanyaan (lihat pembaruan2), tidak ada kesalahan yang ditampilkan, tetapi sepertinya tidak ada yang dikirim ke aplikasi Java/spring. - person Kleber Mota; 10.04.2020
comment
@KleberMota Aneh sekali karena ini berhasil untuk saya. Mungkin ada sesuatu yang berbeda di antara proyek kami, bisakah Anda mengunggah kedua aplikasi ke GitHub selain menunjukkan versi semua perpustakaan yang Anda gunakan? - person eyllanesc; 19.04.2020
comment
2 proyek tersebut adalah: (java) github.com/klebermo/websocket_java, (c++) github.com/klebermo/websocket_cpp - person Kleber Mota; 19.04.2020
comment
@KleberMota Saya telah memperbaiki kode Anda, dan membuat 2 PR untuk repo Anda. - person eyllanesc; 19.04.2020