java/spring 和 c++/qt 应用程序与 websockets 之间的通信

Posted

技术标签:

【中文标题】java/spring 和 c++/qt 应用程序与 websockets 之间的通信【英文标题】:Comunication between java/spring and c++/qt applications with websockets 【发布时间】:2020-04-09 18:15:04 【问题描述】:

我正在尝试使用带有 java/web 应用程序的 spring 实现 websockets,以允许它与使用 qt(以及其中的 websockets 库)用 c++ 编写的应用程序交换消息。

我的 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,用户在其中键入要发送到服务器的消息,以及一个@ 987654327@,继续发送数据。

在我的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();

但是当我执行这两个应用程序并尝试为 java/web 应用程序发送消息时,什么也没有发生。我很确定我犯的错误是在 c++/qt 方面,因为在 java/spring 方面我有一个 html/javascript 代码,可以让我测试消息交换,nd 工作正常。

谁能告诉我我在这里做错了什么?

更新:最小的可重现示例 - java/spring

项目可以用start.spring.io生成,只有spring-websocket作为依赖。除了我上面已经添加的 2 个文件之外,该项目还有:

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>

资源/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);

使用mvn package 构建后,只需使用java -jar target/app.jar 运行即可。

更新:最小的可重现示例 - c++/qt

项目是使用 qt-creator 创建的,类型为 qt-widget。它将创建一个包含 5 个文件的项目:websocket.promainwindow.uimainwindow.hmainwindow.cppmain.cpp

打开mainwindow.ui 并从工具栏中添加lineEditpushButton。右键单击按钮并选择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")));

【问题讨论】:

不确定但是...我猜QWebSocket::sendTextMessage 实际上并没有立即发送消息。相反,它会延迟发送,直到您将控制权返回给 Qt 事件循环。在这种情况下,当您的 QWebSocket 超出范围时,消息可能会丢失。 我该如何解决这个问题? 如果没有minimal reproducible example 就很难回答,但是,请尝试将QWebSocket m_webSocket 设为MainWindow 类的数据成员。 不知道是否有帮助,我尝试更新我的问题以包含这个最小的可重现示例,因为仅使 m_websocket 成为 MainWindow 的数据成员不会使这项工作。跨度> @KleberMota 你说“什么都没有发生”,这是什么意思?你希望得到什么? 【参考方案1】:

问题是您试图在未验证连接是否成功的情况下发送文本。解决方案是使用已连接信号,此外还按照 cmets 中的建议使 m_webSocket 成为该类的成员:

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

更新:

在您的项目中,我注意到以下错误:

由于某种原因,当我使用 Google Chrome 进行测试时,我无法连接,所以我在配置中添加了 registry.addHandler(new SocketHandler(), "/name").setAllowedOrigins("*");

变量“session”仅处理向套接字发送数据,如果您想将该信息发送到所有套接字(js 和 qt),则必须进行迭代。

当会话断开连接时,不要将其从“会话”中删除,这可能会导致错误。您必须在 afterConnectionClosed 方法中删除会话。

在您的代码中,您调用连接到与已连接信号关联的插槽中的服务器,这是愚蠢的,因为该插槽在连接后被调用,为此您应该首先调用 open 方法。无论如何打开连接,等待连接建立,发送消息并关闭连接不是一个好主意,最好在发送消息之前打开连接并在必要时关闭它(例如在关闭 GUI 或用户希望像在 js 中那样关闭它,因为信息的发送不是即时的,而是异步的)。

完整代码为here。

【讨论】:

嗯,有趣的是,当我复制/粘贴您的代码时,c++/qt 应用程序似乎到达了 java/spring 应用程序,但我收到了这个错误:com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $。如果我将代码更改为添加到问题中的代码(请参阅 update2),则不会显示任何错误,但似乎没有任何内容发送到 java/spring 应用程序。 @KleberMota 多么奇怪,因为它对我有用。可能我们的项目之间有什么不同,除了指出你使用的所有库的版本之外,你能把两个应用程序都上传到 GitHub 吗? 这两个项目是:(java) github.com/klebermo/websocket_java, (c++) github.com/klebermo/websocket_cpp @KleberMota 我已经更正了您的代码,并为您的存储库创建了 2 个 PR。

以上是关于java/spring 和 c++/qt 应用程序与 websockets 之间的通信的主要内容,如果未能解决你的问题,请参考以下文章

在 c 程序和 C++ Qt 应用程序之间使用啥 Linux IPC?

带有 Qt/c++ 和 openseemap 的桌面地图应用程序

C++ 中的图形应用程序和 C 中的代码 **Qt 编程**

写了一个QT程序和一个C程序,如何把QT程序里发出的QByteArray类型数据,让C程序接收到或者是处理识别?

使用 Qt gui 绑定 C 代码 [重复]

QT与C程序的调用