WebSocket 协议编程实战

一、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对象

  1. 代码实现
#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的交互得以高效管理。整体结构清晰,便于扩展和维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值