การสื่อสารระหว่างแอปพลิเคชัน Java/spring และ c++/qt ด้วย websockets

ฉันกำลังลองใช้ websockets โดยใช้ spring กับ java/เว็บแอปพลิเคชัน เพื่ออนุญาตให้แลกเปลี่ยนข้อความกับแอปพลิเคชันที่เขียนด้วย c++ โดยใช้ qt (และไลบรารี websockets จากนั้น)

ฉันมีการกำหนดค่านี้ในแอปพลิเคชัน java/spring:

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

และฉันสร้างโปรเจ็กต์ qt-creator ที่เรียบง่ายมากด้วยฟังก์ชัน main และหนึ่งคลาส MainWindow โดยมีสองอ็อบเจ็กต์: lineEdit โดยที่ผู้ใช้พิมพ์ข้อความเพื่อส่งไปยังเซิร์ฟเวอร์ และ pushButton เพื่อดำเนินการส่งข้อมูลต่อไป

ในคลาส MainWindow ของฉัน ฉันใช้สล็อตนี้เพื่อจัดการการแลกเปลี่ยนข้อมูล:

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

แต่เมื่อฉันรันทั้งสองแอปพลิเคชั่น และลองส่งข้อความสำหรับจาวา/เว็บแอปพลิเคชั่น ก็ไม่มีอะไรเกิดขึ้น ฉันค่อนข้างแน่ใจว่าข้อผิดพลาดที่ฉันทำนั้นอยู่ที่ฝั่ง c++/qt เนื่องจากในด้าน java/spring ฉันมีรหัส html/javascript ซึ่งอนุญาตให้ฉันทดสอบการแลกเปลี่ยนข้อความและทำงานได้ดี

ใครสามารถบอกฉันว่าฉันทำอะไรผิดที่นี่?

อัปเดต: ตัวอย่างที่สามารถทำซ้ำได้น้อยที่สุด - java/spring

สามารถสร้างโปรเจ็กต์ได้ด้วย start.spring.io โดยมี spring-websocket เป็นการพึ่งพาเท่านั้น นอกจาก 2 ไฟล์ที่ฉันเพิ่มไว้ข้างต้นแล้ว โปรเจ็กต์นี้จะมี:

ทรัพยากร/static/index.html

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

ทรัพยากร/app.js

ต่างกันอย่างไร;

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

หลังจากสร้างด้วย mvn package ให้รันด้วย java -jar target/app.jar

อัปเดต: ตัวอย่างที่ทำซ้ำได้น้อยที่สุด - c++/qt

โปรเจ็กต์ถูกสร้างขึ้นด้วย qt-creator เป็นประเภท qt-widget มันจะสร้างโปรเจ็กต์ที่มี 5 ไฟล์: websocket.pro, mainwindow.ui, mainwindow.h, mainwindow.cpp และ main.cpp

เปิด mainwindow.ui และเพิ่ม lineEdit และ pushButton จากแถบเครื่องมือ คลิกขวาที่ปุ่มกดแล้วเลือก Go to slot และเลือก clicked() เพิ่มรหัสด้านบน

อัปเดต 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 แหล่งที่มา
comment
ไม่แน่ใจ แต่... ฉันเดาว่า QWebSocket::sendTextMessage จะไม่ส่งข้อความทันที แต่จะเลื่อนการส่งจนกว่าคุณจะกลับการควบคุมไปยังลูปเหตุการณ์ Qt ในกรณีนี้ข้อความอาจหายไปเมื่อ QWebSocket ของคุณอยู่นอกขอบเขต   -  person G.M.    schedule 09.04.2020
comment
ฉันจะแก้ไขสิ่งนั้นได้อย่างไร?   -  person Kleber Mota    schedule 10.04.2020
comment
ยากที่จะตอบโดยไม่มีตัวอย่างที่ทำซ้ำได้น้อยที่สุด แต่ให้ลองทำให้ QWebSocket m_webSocket เป็นสมาชิกข้อมูลของคลาส MainWindow   -  person G.M.    schedule 10.04.2020
comment
ไม่รู้ว่าจะช่วยอะไรได้บ้าง ฉันลองอัปเดตคำถามของฉันเพื่อรวมตัวอย่างที่ทำซ้ำได้น้อยที่สุดนี้ เนื่องจากการทำให้ m_websocket เป็นสมาชิกข้อมูลของ MainWindow เท่านั้นไม่ได้ทำให้สิ่งนี้ทำงานได้   -  person Kleber Mota    schedule 10.04.2020
comment
@KleberMota คุณพูดว่าไม่มีอะไรเกิดขึ้น คุณหมายถึงอะไร? คุณหวังว่าจะได้อะไร?   -  person eyllanesc    schedule 19.04.2020
comment
เมื่อฉันส่งข้อความในแอปพลิเคชัน c++/qt ควรแสดงบน java/spring เหมือนกับว่าฉันใช้เว็บอินเตอร์เฟสที่มีอยู่ในโปรเจ็กต์ java/spring   -  person Kleber Mota    schedule 19.04.2020
comment
คุณพบข้อผิดพลาดอะไร? ในรหัส c ++ หรือ java? ที่นี่ทั้งคู่กำลังรวบรวมโดยไม่มีปัญหา   -  person Kleber Mota    schedule 19.04.2020


คำตอบ (1)


ปัญหาคือคุณกำลังพยายามส่งข้อความโดยไม่ตรวจสอบว่าการเชื่อมต่อสำเร็จ วิธีแก้ไขคือใช้สัญญาณที่เชื่อมต่อ นอกเหนือจากการทำให้ m_webSocket เป็นสมาชิกของคลาสตามที่แนะนำในความคิดเห็น:

*.ชม.

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

อัปเดต:

ในโครงการของคุณ ฉันสังเกตเห็นข้อผิดพลาดต่อไปนี้:

  • ด้วยเหตุผลบางอย่างเมื่อฉันทดสอบโดยใช้ Google Chrome ฉันไม่สามารถเชื่อมต่อได้ดังนั้นฉันจึงเพิ่ม registry.addHandler(new SocketHandler(), "/name").setAllowedOrigins("*"); ในการกำหนดค่า

  • ตัวแปร "เซสชัน" จัดการเฉพาะการส่งข้อมูลไปยังซ็อกเก็ตเท่านั้น หากคุณต้องการส่งข้อมูลนั้นไปยังซ็อกเก็ตทั้งหมด (js และ qt) คุณต้องวนซ้ำ

  • เมื่อเซสชันถูกตัดการเชื่อมต่อ อย่าลบออกจาก "เซสชัน" ซึ่งอาจทำให้เกิดข้อผิดพลาดได้ คุณต้องลบเซสชันออกโดยใช้วิธี afterConnectionClosed

  • ในโค้ดของคุณ คุณกำลังเรียกใช้เพื่อเชื่อมต่อกับเซิร์ฟเวอร์ในช่องที่เกี่ยวข้องกับสัญญาณที่เชื่อมต่อ ซึ่งโง่มากเนื่องจากช่องนั้นถูกเรียกหลังจากการเชื่อมต่อ และสำหรับสิ่งนี้ คุณควรเรียกวิธีเปิดก่อน การเปิดการเชื่อมต่อ รอสร้างการเชื่อมต่อ การส่งข้อความและการปิดการเชื่อมต่อนั้นไม่ใช่ความคิดที่ดี ควรเปิดการเชื่อมต่อก่อนส่งข้อความและปิดเมื่อจำเป็น (เช่น เมื่อปิด GUI หรือ ผู้ใช้ต้องการปิดเหมือนที่ทำใน js เนื่องจากการส่งข้อมูลไม่ได้เกิดขึ้นทันที แต่เป็นแบบอะซิงโครนัส)

โค้ดที่สมบูรณ์อยู่ที่นี่ที่นี่

person eyllanesc    schedule 10.04.2020
comment
ที่น่าสนใจคือเมื่อฉันคัดลอก/วางโค้ดของคุณ ดูเหมือนว่าแอปพลิเคชัน c++/qt จะเข้าถึงแอปพลิเคชัน java/spring แต่ฉันได้รับข้อผิดพลาดนี้: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ หากฉันเปลี่ยนรหัสเป็นคำถามที่เพิ่ม (ดูการอัปเดต 2) จะไม่มีการแสดงข้อผิดพลาด แต่ดูเหมือนว่าจะไม่มีอะไรถูกส่งไปยังแอปพลิเคชัน java/spring - person Kleber Mota; 10.04.2020
comment
@KleberMota ช่างแปลกเหลือเกินเพราะมันใช้ได้ผลสำหรับฉัน อาจมีบางอย่างที่แตกต่างกันระหว่างโครงการของเรา คุณสามารถอัปโหลดทั้งสองแอปพลิเคชันไปยัง GitHub นอกเหนือจากการชี้ให้เห็นเวอร์ชันของไลบรารีทั้งหมดที่คุณใช้ได้หรือไม่ - person eyllanesc; 19.04.2020
comment
2 โครงการคือ: (java) github.com/klebermo/websocket_java, (c++) github.com/klebermo/websocket_cpp - person Kleber Mota; 19.04.2020
comment
@KleberMota ฉันได้แก้ไขรหัสของคุณแล้วและสร้าง PR 2 รายการสำหรับ repos ของคุณ - person eyllanesc; 19.04.2020