1.引言
为什么选择Qt进行网络通信开发?
Qt 提供了一套跨平台、功能完备的网络模块,具有以下优势:
Qt 提供了一套跨平台、功能完备的网络模块,具有以下优势:
- 集成度高 :Qt 自带了丰富的网络通信类(如
QNetworkAccessManager
,QTcpSocket
,QWebSocket
等),使用方便。 - 跨平台统一 API:一次开发,兼容 Windows、Linux、macOS。
- 信号槽机制天然适合事件驱动通信:无需手动管理线程与回调。
- 配合 JSON 库可快速实现类 HTTP 风格通信:语义清晰、开发效率高。
为什么选择这个技术栈?
- Qt WebSockets :
- 基于事件驱动的信号槽机制,避免复杂的线程同步;
- 跨平台支持(Windows/Linux/macOS/嵌入式);
- 与Qt其他模块(如GUI、数据库)无缝集成。
- nlohmann/json :
- 单头文件、零依赖,集成成本极低;
- 提供类似STL的直观API(如
j["key"] = value
); - 性能优于Qt的
QJsonDocument
(尤其在处理大型数据时)。
2.开发环境与依赖准备
在正式编码前,我们需要搭建好开发环境,并引入通信所需的核心模块与库。
2.1 基础环境准备
项目 | 要求版本 | 说明 |
---|---|---|
操作系统 | Windows 10/11 | 推荐用于桌面 Qt 开发 |
Qt | Qt 6.5+ | 必须勾选 Qt WebSockets 模块 |
IDE | Visual Studio 2022 | 支持 Qt 插件与 CMake / qmake |
编译器 | MSVC 2022 (x64) | 默认由 VS 提供,Qt 也会自动适配 |
nlohmann/json下载
可以直接从github上下载或通过镜像网站,这里提供github地址:
这里只需要使用json.hpp
,只下载该资源就可。
多机测试
由于需要测试跨设备通信,因此需要准备两台设备,使用VMware或其他工具搭建虚拟机即可。搭建完毕,先测试设备之间能否连通。如果是两台物理设备,直接测试即可。
ping不通怎么办?
(1)检查IP,确保设备IP为同一子网、子网掩码一致,如果不在可以手动设置IP或者连接同一路由(一般会在同一子网)。
(2)配置路由器允许跨子网通信,设置完毕,重启路由。
(3)检查防火墙,临时关闭防火墙测试。
2.2 创建项目
需要创建两个项目分别作为Server和Client,项目结构如下:
bash
qt_http_demo/
├── client/ # 客户端项目
│ ├── client.ui
│ ├── client.h/.cpp
│ ├── client.pro
├── server/ # 服务端项目
│ ├── server.ui
│ ├── server.h/.cpp
│ ├── server.pro
└── thirdparty/ # 外部依赖
└── json.hpp # nlohmann/json
配置项目文件
在项目中添加Qt WebSockets
模块:
右键项目,选择【属性】,【配置属性】,【Qt Project Setting】,【Qt Modules】,下拉选择【Select Modules...】,勾选【Qt WebSockets】。
引入json
库:
将下载好的json.hpp
放到项目所在文件,右键项目选择【添加】,【现有项...】,选择json.hpp
文件。
在项目中添加该头文件,并重新命名:
c++
#include "json.hpp"
using json = nlohmann::json;
2.3 测试模块
测试代码,检查模块是否可以正常工作:
c++
QWebSocket ws;
json j = {
{"msg","json"}
};
qDebug() << "测试通过:" << QString::fromStdString(j.dump());
如果能够正常运行,就可以写项目了。
3.服务端构建:基于WebSocket的通信响应框架
本章节我们将搭建一个简单的WebSocket服务端,支持多客户端接入。
3.1 UI界面设计
这里使用Qt设计器绘制简单服务器界面:


3.2 代码实现
.h
c++
#pragma once
#include <QtWidgets/QWidget>
#include "ui_WebSocketServerDemo.h"
#include<qwebsocketserver.h>
#include<qwebsocket.h>
#include<qdatetime.h>
#include<qmap.h>
#include<qlist.h>
#include "json.hpp"
using json = nlohmann::json;
class WebSocketServerDemo : public QWidget
{
Q_OBJECT
public:
WebSocketServerDemo(QWidget *parent = nullptr);
~WebSocketServerDemo();
private slots:
void on_btnOpenServer_clicked();
void on_btnCloseServer_clicked();
void on_btnSendMsg_clicked();
void onNewConnection();
void processTextMessage(const QString& message);
void socketDisconnected();
private:
Ui::WebSocketServerDemoClass ui;
QWebSocketServer* m_server;
QList<QWebSocket*> m_clients;
QMap<QString, QWebSocket*> m_clientMap;
QDateTime* m_currentDateTime;
void broadcastMessage(const QString& message, const QString& sender = "");
void sendPrivateMessage(QWebSocket* client, const QString& message);
void updateOnlineUsers();
};
.cpp
C++
#include "WebSocketServerDemo.h"
#include <QMessageBox>
#include <QNetworkInterface>
WebSocketServerDemo::WebSocketServerDemo(QWidget *parent)
: QWidget(parent),m_server(nullptr),m_currentDateTime(new QDateTime())
{
ui.setupUi(this);
this->setWindowTitle("WebSocket 服务器");
//初始化目标下拉框
ui.comboBox_Target->addItem("所有客户端");
ui.comboBox_Target->setCurrentIndex(0);
// 获取无线网卡(WLAN)对应的 IPv4 地址
foreach(const QNetworkInterface & interface, QNetworkInterface::allInterfaces()) {
// 筛选启用的、运行中的接口,避免抓到虚拟机或禁用设备
if (!(interface.flags() & QNetworkInterface::IsUp) ||
!(interface.flags() & QNetworkInterface::IsRunning) ||
(interface.flags() & QNetworkInterface::IsLoopBack)) {
continue;
}
// 匹配无线网卡(根据类型或名称)
if (interface.type() == QNetworkInterface::InterfaceType::Wifi ||
interface.humanReadableName().contains("WLAN", Qt::CaseInsensitive) ||
interface.humanReadableName().contains("无线", Qt::CaseInsensitive)) {
// 遍历此接口的地址列表,找 IPv4
for (const QNetworkAddressEntry& entry : interface.addressEntries()) {
QHostAddress ip = entry.ip();
if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
ui.lineEdit_IP->setText(ip.toString());
qDebug() << "使用无线网卡IP:" << ip.toString();
break;
}
}
}
}
//设置默认端口为8000
ui.lineEdit_Port->setText("8000");
}
WebSocketServerDemo::~WebSocketServerDemo()
{
delete m_currentDateTime;
if (m_server) {
m_server->close();
delete m_server;
}
}
//开启服务
void WebSocketServerDemo::on_btnOpenServer_clicked() {
QString ip = ui.lineEdit_IP->text();
quint16 port = ui.lineEdit_Port->text().toUShort();
// 清理旧服务器实例
if (m_server) {
m_server->close();
delete m_server;
}
// 创建非加密模式的WebSocket服务器
m_server = new QWebSocketServer("WebSocket Server", QWebSocketServer::NonSecureMode, this);
if (m_server->listen(QHostAddress(ip), port)) {
connect(m_server, &QWebSocketServer::newConnection,
this, &WebSocketServerDemo::onNewConnection);
// 更新UI状态
ui.textEdit_SendMsg->append(QString("[系统] 服务已启动在 %1:%2").arg(ip).arg(port));
ui.btnOpenServer->setEnabled(false);
ui.btnCloseServer->setEnabled(true);
}
else {
QMessageBox::critical(this, "错误", QString("无法启动服务: %1").arg(m_server->errorString()));
}
}
//关闭服务
void WebSocketServerDemo::on_btnCloseServer_clicked() {
if (m_server && m_server->isListening()) {
// 通知所有客户端
broadcastMessage("[系统] 服务器即将关闭");
// 清理资源
m_server->close();
qDeleteAll(m_clients);
m_clients.clear();
m_clientMap.clear();
ui.listWidget_OnlineUser->clear();
// 更新UI状态
ui.textEdit_SendMsg->append("[系统] 服务已停止");
ui.btnOpenServer->setEnabled(true);
ui.btnCloseServer->setEnabled(false);
}
}
//发送消息
void WebSocketServerDemo::on_btnSendMsg_clicked() {
QString message = ui.textEdit_SendMsg->toPlainText();
if (message.isEmpty()) return;
QString time = m_currentDateTime->currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
//获取当前选中的目标
QString target = ui.comboBox_Target->currentText();
if (!target.isEmpty() && target != "所有客户端") {
//定点发送
if (m_clientMap.contains(target)) {
json privateMsg = {
{"type","private"},
{"timestamp",time.toStdString()},
{"content",message.toStdString()},
{"target",target.toStdString()}
};
sendPrivateMessage(m_clientMap[target], QString::fromStdString(privateMsg.dump()));
// 记录发送日志
ui.listWidget_RecvMsg->addItem(QString("[%1] [私信→%2] %3")
.arg(time)
.arg(target)
.arg(message));
}
else {
// 目标无效时的反馈
ui.listWidget_RecvMsg->addItem(QString("[%1] [错误] 目标客户端不存在: %2")
.arg(time)
.arg(target));
}
}
else
{
// 群发消息
broadcastMessage(message, "服务器");
ui.listWidget_RecvMsg->addItem(QString("[%1] [群发] %2")
.arg(time)
.arg(message));
}
ui.textEdit_SendMsg->clear();
}
//客户端连接
void WebSocketServerDemo::onNewConnection() {
QWebSocket* client = m_server->nextPendingConnection();
if (!client) return;
// 连接信号槽
connect(client, &QWebSocket::textMessageReceived, this, &WebSocketServerDemo::processTextMessage);
connect(client, &QWebSocket::disconnected, this, &WebSocketServerDemo::socketDisconnected);
// 生成客户端ID(IP:Port格式)
QString clientId = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
// 记录客户端
m_clients.append(client);
m_clientMap.insert(clientId, client);
//更新目标下拉框
ui.comboBox_Target->addItem(clientId);
//记录连接日志
QString time = m_currentDateTime->currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QString msg = QString("[%1] %2 已连接").arg(time).arg(clientId);
ui.listWidget_RecvMsg->addItem(msg);
ui.textEdit_SendMsg->append(msg);
updateOnlineUsers();// 刷新在线列表
//发送欢迎消息(JSON格式)
json welcomeMsg = {
{"type","system"},
{"timestamp",time.toStdString()},
{"content","欢迎连接到WebSocket服务器"}
};
client->sendTextMessage(QString::fromStdString(welcomeMsg.dump()));
}
//接收客户端消息
void WebSocketServerDemo::processTextMessage(const QString& message) {
QWebSocket* client = qobject_cast<QWebSocket*>(sender());
if (!client)return;
QString clientId = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
QString time = m_currentDateTime->currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
try {
// 解析JSON消息
json msg = json::parse(message.toStdString());
if (msg.contains("type") && msg["type"] == "chat") {
QString content = QString::fromStdString(msg.value("content", ""));
//显示在接收消息框
QString displayMsg = QString("[%1] %2:%3").arg(time).arg(clientId).arg(content);
ui.listWidget_RecvMsg->addItem(displayMsg);
//处理广播请求
if (msg.value("broadcast", false)) {
broadcastMessage(content, clientId);
}
}
}
catch(const json::exception& e){
// JSON解析错误处理
QString errorMsg = QString("[%1] %2: JSON解析错误 - %3")
.arg(time)
.arg(clientId)
.arg(e.what());
ui.listWidget_RecvMsg->addItem(errorMsg);
client->sendTextMessage(R"({"error": "Invalid message format"})");
}
}
//客户端断开
void WebSocketServerDemo::socketDisconnected() {
QWebSocket* client = qobject_cast<QWebSocket*>(sender());
if (!client) return;
// 获取客户端信息
QString clientId = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
// 记录断开日志
QString time = m_currentDateTime->currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
QString msg = QString("[%1] %2 已断开连接").arg(time).arg(clientId);
ui.listWidget_RecvMsg->addItem(msg);
ui.textEdit_SendMsg->append(msg);
// 清理资源
m_clients.removeAll(client);
m_clientMap.remove(clientId);
int index = ui.comboBox_Target->findText(clientId);
// 从下拉框移除
if (index != -1) {
ui.comboBox_Target->removeItem(index);
}
client->deleteLater();
updateOnlineUsers();// 刷新在线列表
}
//广播消息
void WebSocketServerDemo::broadcastMessage(const QString& message, const QString& sender) {
QString time = m_currentDateTime->currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
json broadcastMsg = {
{"type", "broadcast"},
{"timestamp", time.toStdString()},
{"content", message.toStdString()},
{"sender", sender.toStdString()}
};
// 向所有有效客户端发送
QString msgStr = QString::fromStdString(broadcastMsg.dump());
for (QWebSocket* client : m_clients) {
if (client->isValid()) {
client->sendTextMessage(msgStr);
}
}
}
//私发消息
void WebSocketServerDemo::sendPrivateMessage(QWebSocket* client, const QString& message) {
if (client && client->isValid()) {
client->sendTextMessage(message);
}
}
//更新在线列表
void WebSocketServerDemo::updateOnlineUsers() {
ui.listWidget_OnlineUser->clear();
for (const QString& clientId : m_clientMap.keys()) {
ui.listWidget_OnlineUser->addItem(clientId);
}
}
4.客户端构建与数据通信
本章将搭建一个简单的WebSocket客户端,实现与服务端的连接与数据通信,结合nlohmann/json
实现数据传输。
4.1 UI界面设计
使用Qt设计器绘制简单服务器界面:


4.2代码实现
.h
c++
#pragma once
#include <QtWidgets/QWidget>
#include "ui_WebSocketClientDmeo.h"
#include<qwebsocket.h>
#include<qdatetime.h>
#include"json.hpp"
using json = nlohmann::json;
class WebSocketClientDmeo : public QWidget
{
Q_OBJECT
public:
WebSocketClientDmeo(QWidget *parent = nullptr);
~WebSocketClientDmeo();
private slots:
void on_btnConnect_clicked();
void on_btnDisconnect_clicked();
void on_btnSendMsg_clicked();
void onConnected();
void onDisconnected();
void onTextMessageReceived(const QString& message);
void onError(QAbstractSocket::SocketError error);
private:
Ui::WebSocketClientDmeoClass ui;
QWebSocket* m_socket;
QDateTime m_currentDateTime;
bool m_isConnected;
void updateStatus(const QString& message, bool isError = false);
void appendMessage(const QString& message);
};
.cpp
c++
#include "WebSocketClientDmeo.h"
#include <qurl.h>
#include <qmessagebox.h>
WebSocketClientDmeo::WebSocketClientDmeo(QWidget *parent)
: QWidget(parent),m_socket(nullptr),m_isConnected(false)
{
ui.setupUi(this);
this->setWindowTitle("WebSocket 客户端");
// 设置默认URL:服务器IP
ui.lineEdit_URL->setText("ws://192.168.2.45:8000");
updateStatus("准备就绪");
//初始状态控制
ui.btnDisconnect->setEnabled(false);
}
WebSocketClientDmeo::~WebSocketClientDmeo()
{
if (m_socket) {
m_socket->deleteLater();
}
}
// 连接按钮点击事件
void WebSocketClientDmeo::on_btnConnect_clicked() {
if (m_isConnected) return;// 避免重复连接
QString urlStr = ui.lineEdit_URL->text().trimmed();
if (urlStr.isEmpty()) {
updateStatus("URL不能为空", true); // 错误提示(红色)
return;
}
QUrl url(urlStr);
if (!url.isValid()) {
updateStatus("无效的URL格式", true);
return;
}
// 清理旧连接(如果存在)
if (m_socket) {
m_socket->deleteLater();
}
// 创建新的WebSocket连接
m_socket = new QWebSocket();
connect(m_socket, &QWebSocket::connected, this, &WebSocketClientDmeo::onConnected);
connect(m_socket, &QWebSocket::disconnected, this, &WebSocketClientDmeo::onDisconnected);
connect(m_socket, &QWebSocket::textMessageReceived,
this, &WebSocketClientDmeo::onTextMessageReceived);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::errorOccurred),
this, &WebSocketClientDmeo::onError);
updateStatus("正在连接...");
m_socket->open(url);// 发起连接
}
// 断开连接按钮点击事件
void WebSocketClientDmeo::on_btnDisconnect_clicked() {
if (m_socket && m_isConnected) {
m_socket->close();// 主动关闭连接
}
}
// 发送消息按钮点击事件
void WebSocketClientDmeo::on_btnSendMsg_clicked() {
if (!m_isConnected || !m_socket) {
updateStatus("未连接到服务器", true);
return;
}
QString message = ui.textEdit_Send->toPlainText();
if (message.isEmpty()) {
updateStatus("消息内容不能为空", true);
return;
}
try {
// 构造JSON消息
json msg = {
{"type", "chat"}, // 消息类型
{"timestamp", m_currentDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString()},
{"content", message.toStdString()}// 消息内容
};
// 发送消息
m_socket->sendTextMessage(QString::fromStdString(msg.dump()));
// 在接收框显示已发送消息
appendMessage(QString("[发送] %1").arg(message));
ui.textEdit_Send->clear();// 清空输入框
}
catch (const std::exception& e) {
updateStatus(QString("消息发送失败: %1").arg(e.what()), true);
}
}
// 连接成功事件处理
void WebSocketClientDmeo::onConnected() {
m_isConnected = true;
updateStatus("已连接到服务器");
// 更新按钮状态
ui.btnConnect->setEnabled(false);
ui.btnDisconnect->setEnabled(true);
ui.lineEdit_URL->setEnabled(false);// 连接后禁用URL修改
appendMessage("[系统] 连接已建立");
}
// 连接断开事件处理
void WebSocketClientDmeo::onDisconnected() {
m_isConnected = false;
updateStatus("已断开连接");
// 恢复初始按钮状态
ui.btnConnect->setEnabled(true);
ui.btnDisconnect->setEnabled(false);
ui.lineEdit_URL->setEnabled(true);
appendMessage("[系统] 连接已关闭");
}
// 接收消息处理
void WebSocketClientDmeo::onTextMessageReceived(const QString& message) {
try {
// 解析JSON消息
json msg = json::parse(message.toStdString());
// 提取消息字段
QString time = QString::fromStdString(msg.value("timestamp", ""));
QString content = QString::fromStdString(msg.value("content", ""));
QString sender = QString::fromStdString(msg.value("sender", "服务器"));
// 格式化显示消息
QString displayMsg;
if (sender.isEmpty()) {
displayMsg = QString("[%1] %2").arg(time).arg(content);
}
else {
displayMsg = QString("[%1] [来自 %2] %3").arg(time).arg(sender).arg(content);
}
appendMessage(displayMsg);
}
catch (const json::exception& e) {
appendMessage(QString("[错误] 消息解析失败: %1").arg(e.what()));
}
}
// 错误处理
void WebSocketClientDmeo::onError(QAbstractSocket::SocketError error) {
Q_UNUSED(error)// 未使用的参数
updateStatus(QString("连接错误: %1").arg(m_socket->errorString()), true);
appendMessage(QString("[错误] %1").arg(m_socket->errorString()));
}
// 更新状态栏显示
void WebSocketClientDmeo::updateStatus(const QString& message, bool isError) {
ui.label_StatusInformation->setText(message);
if (isError) {
ui.label_StatusInformation->setStyleSheet("color: red;");
}
else {
ui.label_StatusInformation->setStyleSheet("color: green;");
}
}
// 添加消息到接收框
void WebSocketClientDmeo::appendMessage(const QString& message) {
ui.textEdit_Recv->append(message);// 自动换行显示
}
5.效果演示
服务端初始界面

客户端初始界面

服务端开启服务

客户端连接服务

客户端向服务端发送内容


服务端群发内容

客户端私发内容


客户端断开连接

服务器关闭服务

6.总结
在本篇文章中,我们围绕 Qt 的网络通信能力 ,构建了一个完整的基于 WebSocket 的 HTTP 风格客户端与服务器通信示例。通过 nlohmann/json 库实现了结构化数据的传输,使得消息交互更加清晰、模块化,提升了开发效率。
整篇项目涵盖内容包括:
- Qt 网络模块与 WebSocket 客户端/服务器的搭建
- JSON 格式解析与封装,提升通信协议的灵活性
- UI 交互设计,简化客户端操作逻辑
- 多客户端接入管理,实现了私聊/群发功能