Java/Spring与C++/Qt应用程序之间使用WebSocket进行通信。

huangapple go评论102阅读模式
英文:

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

问题

我正在尝试使用Spring和Java/Web应用程序来实现WebSocket,以允许其与使用Qt编写的C++应用程序交换消息(并使用其中的WebSocket库)。

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

但是当我执行这两个应用程序,并尝试向Java/Web应用程序发送消息时,什么都没有发生。我很确定我在C++/Qt方面犯了错误,因为在Java/Spring方面,我有一个允许我测试消息交换的HTML/JavaScript代码,它运行良好。

有人可以告诉我我在这里做错了什么吗?

更新:最小可复现示例 - 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>

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

构建完成后,使用mvn package进行构建,然后使用java -jar target/app.jar运行。

更新:最小可复现示例 - C++/Qt

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

打开mainwindow.ui,从工具栏中添加一个lineEdit和一个pushButton。右键单击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")));
}
英文:

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(), &quot;/name&quot;);
	}
}

SocketHandler.java

@Component
public class SocketHandler extends TextWebSocketHandler {
	List&lt;WebSocketSession&gt; sessions = new CopyOnWriteArrayList&lt;&gt;();

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

	@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-&gt;ui-&gt;lineEdit-&gt;text();
    QWebSocket m_webSocket;
    m_webSocket.open(QUrl(QStringLiteral(&quot;ws://localhost:8080/name&quot;)));
    m_webSocket.sendTextMessage(&quot;Hello &quot; + message + &quot; !&quot;);
    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

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

  &lt;hr&gt;

  &lt;table id=&quot;conversation&quot; border=&quot;2&quot;&gt;
      &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Greetings&lt;/th&gt;
      &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody id=&quot;greetings&quot;&gt;
      &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;script src=&quot;/app.js&quot;&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

resources/app.js

var ws;

function connect() {
	ws = new WebSocket(&#39;ws://localhost:8080/name&#39;);

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

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

function disconnect() {
    if (ws != null)
        ws.close();
      
    document.querySelector(&quot;#connect&quot;).removeAttribute(&quot;disabled&quot;);
    document.querySelector(&quot;#disconnect&quot;).setAttribute(&quot;disabled&quot;, &quot;disabled&quot;);
    document.querySelector(&quot;#conversation&quot;).style.display = &#39;none&#39;;
    document.querySelector(&quot;#greetings&quot;).innerHTML = &quot;&quot;;
}

function send() {
  var name = document.querySelector(&quot;#name&quot;);
	var data = JSON.stringify({&#39;name&#39;: 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-&gt;lineEdit-&gt;text();

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

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

答案1

得分: 2

问题在于您尝试发送文本而未验证连接是否成功。解决方案是在连接信号中使用connected信号,此外还应根据评论中的建议将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),则必须进行迭代。

  • 当会话断开连接时,不要从“sessions”中删除它,否则可能会导致错误。您必须在afterConnectionClosed方法中删除会话。

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

完整代码在此处

英文:

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-&gt;lineEdit-&gt;text();

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

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

<h3>Update:</h3>

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(), &quot;/name&quot;).setAllowedOrigins(&quot;*&quot;); 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).

<sub>The complete code is here.</sub>

huangapple
  • 本文由 发表于 2020年4月10日 02:15:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/61127606.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定