一、WebSocket 基础知识
WebSocket是一种通过单个TCP连接提供全双工通信信道的网 络技术。2011 年,IETF 将 WebSocket 协议标准化为 RFC 6455。 QWebSocket 可用于客户端应用程序和服务器应用程序。
二、案例
服务端
注意:
1.qt工程文件添加模块:
QT += core gui websockets
2.UI设计
3.需要头文件:
#include QAbstractSocket// 它是QTcpSocket和QUdpSocket的基类
#include QWebSocketServer
#include QWebSocket
#include QJsonDocument // 提供读取和写入JSON文档的相关方法
#include QJsonObject // JSON对象
- 代码实现
#include "widget.h" // 包含Widget类的头文件
#include "ui_widget.h" // 包含UI设计文件的头文件
Widget::Widget(QWidget *parent) // Widget构造函数,初始化父类
: QWidget(parent) // 调用父类构造函数
, ui(new Ui::Widget) // 创建UI指针
{
ui->setupUi(this); // 设置UI界面
// WS(WebSocket服务器以非安全模式运行)
// WSS(WebSocket服务器以安全模式运行)
websocketserver=new QWebSocketServer(QStringLiteral("testServer"), // 创建WebSocket服务器实例
QWebSocketServer::NonSecureMode,this);
connect(websocketserver,&QWebSocketServer::newConnection,this,&Widget::getnewconnect); // 连接新连接信号到槽函数
websocketserver->listen(QHostAddress::Any,8899); // 监听任意地址的8899端口
}
Widget::~Widget() // Widget析构函数
{
delete ui; // 释放UI资源
for(auto socket:websocketlist) // 遍历所有WebSocket连接
{
socket->close(); // 关闭每个WebSocket连接
}
websocketserver->close(); // 关闭WebSocket服务器
}
void Widget::on_pushButton_SendData_clicked() // 当发送数据按钮被点击时执行
{
QString strtext=ui->textEdit_SendData->toPlainText().trimmed(); // 获取输入的文本并去除首尾空格
if(strtext.isEmpty()) // 如果文本为空则返回
return;
if(ui->radioButton_SendAll->isChecked()) // 如果选择了群发
{
if(websocketlist.size()==0) // 如果没有连接的客户端则返回
return;
for(auto socket:websocketlist) // 遍历所有连接的WebSocket
{
socket->sendTextMessage(strtext); // 给每个连接发送消息
}
ui->textEdit_MsgList->append("服务器给所有连接发送:"+strtext); // 记录发送的消息
}
else // 私发客户端
{
if(!ui->listWidget_Client->currentItem()) // 如果没有选中的客户端则返回
{
return;
}
QString strcurrent=ui->listWidget_Client->currentItem()->text(); // 获取当前选中客户端的标识
QWebSocket *websocket=nullptr; // 初始化WebSocket指针
for(auto socket:websocketlist) // 遍历所有WebSocket
{
if(socket->origin()==strcurrent) // 如果WebSocket的来源与当前选中项匹配
{
websocket=socket; // 赋值给websocket指针
break; // 找到后退出循环
}
}
if(websocket) // 如果找到对应的WebSocket连接
{
websocket->sendTextMessage(strtext); // 发送消息
ui->textEdit_MsgList->append("服务端给["+websocket->origin()+"]发送--->"+strtext); // 记录发送的消息
}
}
ui->textEdit_SendData->clear(); // 清空输入框
}
void Widget::getnewconnect() // 获取新连接的槽函数
{
// 如果服务器有挂起的连接,返回true
if(websocketserver->hasPendingConnections()) // 检查是否有待处理的连接
{
QWebSocket *websocket=websocketserver->nextPendingConnection(); // 获取下一个待处理的连接
ui->textEdit_MsgList->append(websocket->origin()+"客户端已连接到服务器"); // 记录连接信息
websocketlist<<websocket; // 将新连接添加到连接列表中
QListWidgetItem *item=new QListWidgetItem; // 创建新的列表项
item->setText(websocket->origin()); // 设置列表项文本为客户端来源
ui->listWidget_Client->addItem(item); // 将新客户端添加到客户端列表控件中
connect(websocket,&QWebSocket::disconnected,this,[websocket,this] // 连接断开信号到槽函数
{
ui->textEdit_MsgList->append(websocket->origin()+"客户端已断开服务器"); // 记录断开信息
websocketlist.removeOne(websocket); // 从连接列表中移除断开的连接
for(int i=0;i<ui->listWidget_Client->count();i++) // 遍历客户端列表
{
QListWidgetItem *item=ui->listWidget_Client->item(i); // 获取列表项
if(item->text()==websocket->origin()) // 如果列表项匹配
{
ui->listWidget_Client->removeItemWidget(item); // 从列表中移除控件
delete item; // 删除列表项
break; // 退出循环
}
}
websocket->deleteLater(); // 延迟删除WebSocket对象
});
connect(websocket,&QWebSocket::textMessageReceived,this,&Widget::receiveMsg); // 连接消息接收信号到槽函数
connect(websocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onerrorFunc(QAbstractSocket::SocketError))); // 连接错误信号到槽函数
}
}
void Widget::receiveMsg(const QString &msg) // 消息接收的槽函数
{
QJsonDocument jd=QJsonDocument::fromJson(msg.toLatin1().data()); // 将接收到的消息解析为JSON文档
if(jd.isNull()) // 如果解析失败
{
QWebSocket *websocket=qobject_cast<QWebSocket*>(sender()); // 获取发送该消息的WebSocket
ui->textEdit_MsgList->append("收到客户端消息["+websocket->origin()+"]--->"+msg); // 记录收到的消息
}
else // 如果解析成功
{
QJsonObject jdo=jd.object(); // 获取JSON对象
QString dst=jdo["dst:"].toString(); // 获取目标客户端
for (auto socket:websocketlist) // 遍历所有连接的WebSocket
{
if(dst==socket->origin()) // 如果目标匹配
socket->sendTextMessage(msg); // 将消息发送给目标客户端
}
}
}
void Widget::onerrorFunc(QAbstractSocket::SocketError error) // 错误处理槽函数
{
QWebSocket *websocket=qobject_cast<QWebSocket*>(sender()); // 获取出错的WebSocket连接
ui->textEdit_MsgList->append(websocket->origin()+"出错"+websocket->errorString()); // 记录错误
}
代码结构分析
类构造与析构:
构造函数初始化界面,创建WebSocket服务器,并开始监听连接。
析构函数清理资源,确保连接关闭,避免内存泄漏。
信号与槽机制:
使用Qt的信号与槽机制处理新连接、消息接收和错误处理。
通过connect函数将信号与相应的槽函数连接,确保在事件发生时能够响应。
数据发送功能:
提供群发和私发功能,使用条件语句判断用户选择的发送方式。
通过QWebSocket对象向连接的客户端发送文本消息。
连接管理:
使用QList存储所有连接的WebSocket,方便进行管理和消息发送。
在新连接建立时,添加到列表中并更新UI,在断开时移除连接并更新UI。
消息处理:
使用JSON格式来处理接收到的消息,能够根据消息内容决定转发目标。
提供消息接收和解析机制,确保接收到的消息能够被正确处理。
错误处理:
实现了基本的错误处理机制,能够捕获并记录WebSocket连接中的错误信息。
总结
该代码实现了一个基本的WebSocket服务器,支持多个客户端连接,能够进行群发和私发消息,同时具备连接管理、消息接收和错误处理的能力。整体结构清晰,利用Qt的特性实现了高效的事件管理和UI更新。
客户端
注意:
工程文件添加:
QT += core gui websockets
UI设计:
头文件包含:
#include QAbstractSocket
#include QWebSocket
#include QJsonDocument
#include QJsonObject
代码
#include "widget.h" // 包含Widget类的头文件
#include "ui_widget.h" // 包含UI界面的头文件
#include <QMessageBox> // 引入消息框类,以便显示错误信息
// Widget类的构造函数
Widget::Widget(QWidget *parent)
: QWidget(parent) // 调用父类构造函数
, ui(new Ui::Widget) // 创建UI对象
{
ui->setupUi(this); // 初始化UI界面
websocket = nullptr; // 初始化websocket指针为nullptr
}
// Widget类的析构函数
Widget::~Widget()
{
delete ui; // 删除UI对象
if (websocket) // 如果websocket存在
websocket->close(); // 关闭websocket连接
}
// 接收消息的处理函数
void Widget::receviedMsgFunc(const QString &msg) // 接收消息
{
QJsonDocument jsd = QJsonDocument::fromJson(msg.toUtf8().data()); // 将消息转换为JSON文档
if (jsd.isNull()) // 如果JSON文档为空
{
ui->textEdit_MsgList->append("收到消息:" + msg); // 直接显示消息
}
else // 如果是有效的JSON格式
{
QJsonObject jsobj = jsd.object(); // 获取JSON对象
ui->textEdit_MsgList->append("收到来自" + jsobj["SRC"].toString() + "的消息:" + jsobj["MSG"].toString()); // 显示来源和内容
}
}
// 处理websocket错误的函数
void Widget::onerrorFunc(QAbstractSocket::SocketError error)
{
// 向消息列表中添加错误信息
ui->textEdit_MsgList->append(websocket->origin() + "出错" + websocket->errorString());
}
// 连接和断开服务器的按钮点击事件处理
void Widget::on_pushButton_Connect_clicked()
{
if (!websocket) // 如果websocket尚未创建
{
// 判断服务器名称是否为空
if (ui->lineEdit_ServerName->text().trimmed().isEmpty())
{
QMessageBox::critical(this, "错误", "服务器名称不能为空,请重新检查!", QMessageBox::Yes); // 弹出错误提示
return; // 返回,结束函数
}
// 创建websocket对象并指定服务器地址
websocket = new QWebSocket(ui->lineEdit_ServerName->text().trimmed(), QWebSocketProtocol::VersionLatest, this);
// 连接成功的信号槽
connect(websocket, &QWebSocket::connected, this, [this]
{
ui->textEdit_MsgList->append("已经连接上" + websocket->peerAddress().toString()); // 显示连接成功消息
bConnect = true; // 设置连接状态为true
ui->pushButton_Connect->setText("断开服务器"); // 更新按钮文本
});
// 断开连接的信号槽
connect(websocket, &QWebSocket::disconnected, this, [this]
{
ui->textEdit_MsgList->append("已" + websocket->peerAddress().toString() + "断开连接"); // 显示断开连接消息
bConnect = false; // 设置连接状态为false
ui->pushButton_Connect->setText("连接服务器"); // 更新按钮文本
});
// 连接错误信号
connect(websocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onerrorFunc(QAbstractSocket::SocketError)));
// 连接消息接收信号
connect(websocket, &QWebSocket::textMessageReceived, this, &Widget::receviedMsgFunc);
}
// 如果当前未连接
if (!bConnect)
websocket->open(QUrl(ui->lineEdit_ServerAddress->text().trimmed())); // 打开websocket连接
else
{
websocket->close(); // 关闭连接
websocket->deleteLater(); // 延迟删除websocket对象
websocket = nullptr; // 将websocket指针设为nullptr
}
}
// 发送消息按钮点击事件处理
void Widget::on_pushButton_SendMsg_clicked()
{
if (!websocket) // 如果websocket不存在
return; // 返回
if (!websocket->isValid()) // 如果websocket无效
return; // 返回
// 获取发送的文本信息
QString strtext = ui->textEdit_SendData->toPlainText().trimmed();
if (strtext.isEmpty()) // 如果发送文本为空
return; // 返回
// 获取客户端名称
QString strclient = ui->lineEdit_ClientName->text().trimmed();
if (strclient.isEmpty()) // 如果客户端名称为空
{
websocket->sendTextMessage(strtext); // 直接发送消息
ui->textEdit_MsgList->append("发送消息:" + strtext); // 显示发送的消息
}
else // 如果指定了特定客户端
{
QJsonObject jsobj; // 创建JSON对象
jsobj["src"] = websocket->origin(); // 设置消息来源
jsobj["dst"] = strclient; // 设置消息目标
jsobj["msg"] = strtext; // 设置消息内容
websocket->sendTextMessage(QString(QJsonDocument(jsobj).toJson(QJsonDocument::Compact))); // 发送JSON格式的消息
ui->textEdit_MsgList->append("给客户端" + strclient + "发送消息:" + strtext); // 显示发送的消息信息
}
ui->textEdit_SendData->clear(); // 清空发送文本框
}
代码结构分析
类定义与成员变量:
Widget类继承自QWidget,负责处理用户界面和与WebSocket的交互。
使用了QWebSocket类来处理WebSocket连接,ui指针用于管理Qt Designer生成的UI界面。
构造与析构函数:
在构造函数中初始化UI和WebSocket指针。
在析构函数中清理资源,关闭WebSocket连接。
消息接收与错误处理:
receviedMsgFunc处理接收到的消息,将其解析为JSON对象并展示在消息列表中。
onerrorFunc处理WebSocket错误,通过消息框显示错误信息。
连接与断开功能:
on_pushButton_Connect_clicked函数处理用户点击连接或断开按钮的事件。
通过QWebSocket的信号与槽机制,处理连接成功、断开和错误事件,更新UI上的状态。
发送消息:
on_pushButton_SendMsg_clicked函数处理发送消息的逻辑,支持直接发送文本或通过JSON格式发送给指定客户端。
总结
该程序实现了一个基本的WebSocket客户端功能,支持连接到服务器、接收和发送消息,同时具备错误处理机制。通过信号与槽机制,UI和WebSocket的交互得以高效管理。整体结构清晰,便于扩展和维护。