WebSocket的原理及QT示例

发布于:2025-05-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

一.WebSocket 介绍

1.概述

WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议,它在 2011 年被 IETF 定为标准 RFC 6455,并由 RFC7936 补充规范。与传统的 HTTP 协议不同,WebSocket 允许服务器和客户端之间进行实时、双向的数据传输,打破了 HTTP 协议请求 - 响应的模式限制,大大提高了数据传输的效率和实时性。

2.特点

全双工通信:服务器和客户端可以在任意时刻向对方发送数据,无需等待对方的请求。

实时性强:由于采用了全双工通信,数据可以即时传输,非常适合实时性要求较高的应用场景,如在线聊天、实时数据监控等。

较少的开销:WebSocket 握手阶段使用 HTTP 协议,建立连接后,数据传输不再需要像 HTTP 那样携带大量的头部信息,减少了数据传输的开销。

跨域支持:WebSocket 支持跨域通信,方便不同域名之间的服务器和客户端进行数据交互。

客户端与服务器交互图

3.应用场景

实时聊天应用:如在线客服、社交聊天等,用户可以即时收到对方发送的消息。

实时数据监控:如股票行情、传感器数据监控等,服务器可以实时将最新的数据推送给客户端。

多人在线游戏:实现游戏中玩家之间的实时交互,如位置更新、动作同步等。

二.代码示例

1.客户端代码

#include "Clientdialog.h"

#include <QLabel>

#include <QWidget>

#include <QHBoxLayout>

#include <QVBoxLayout>

#include <QtCore>

#include <QDebug>

#include <iostream>

ClientDialog::ClientDialog(QWidget *parent)

    : QWidget(parent)

{

    //layout1

    QLabel *iplabel = new QLabel("Server IP");

    m_iplineedit =new QLineEdit;

    m_iplineedit->setText("127.0.0.1");

    QLabel *portlabel =new QLabel("Server端口");

    m_portspinbox = new QSpinBox;

    m_portspinbox->setRange(0,65535);

    m_portspinbox->setValue(5123);

    m_linkbutton = new QPushButton("连接");

    m_disconnectbutton = new QPushButton("断开");

    pButtonGroup = new QButtonGroup();

    pButtonGroup->setExclusive(true);

    m_linkbutton->setCheckable(true);

    m_disconnectbutton->setCheckable(true);

    pButtonGroup->addButton(m_linkbutton,0);

    pButtonGroup->addButton(m_disconnectbutton,1);

    QHBoxLayout *qhboxlayout1 = new QHBoxLayout;

    qhboxlayout1->addWidget(iplabel);

    qhboxlayout1->addWidget(m_iplineedit);

    qhboxlayout1->addWidget(portlabel);

    qhboxlayout1->addWidget(m_portspinbox);

    qhboxlayout1->addWidget(m_linkbutton);

    qhboxlayout1->addWidget(m_disconnectbutton);

    //layout2

    QLabel *sendmessagelabel = new QLabel("发送消息");

    QHBoxLayout *qhboxlayout2 = new QHBoxLayout;

    qhboxlayout2->addWidget(sendmessagelabel);

    //layout3

    m_sendmessagetextedit = new QTextEdit;

    m_sendmessagetextedit->setFixedHeight(50);

    m_sendbutton = new QPushButton("发送");

    m_sendbutton->setFixedHeight(50);

    QHBoxLayout *qhboxlayout3 = new QHBoxLayout;

    qhboxlayout3->addWidget(m_sendmessagetextedit);

    qhboxlayout3->addWidget(m_sendbutton);

    //layout4

    QLabel *receivemessagelabel = new QLabel("接收消息");

    QHBoxLayout *qhboxlayout4 = new QHBoxLayout;

    qhboxlayout4->addWidget(receivemessagelabel);

    //layout5

    m_receivemessageTextEdit = new QTextEdit;

    QHBoxLayout *qhboxlayout5 = new QHBoxLayout;

    qhboxlayout5->addWidget(m_receivemessageTextEdit);

    m_receivemessageTextEdit->setReadOnly(true);

    //layout6

    statusLabel = new QLabel("连接状态");

    m_clean = new QPushButton("清除");

    QHBoxLayout *qhboxlayout6 = new QHBoxLayout;

    qhboxlayout6->addWidget(statusLabel);

    qhboxlayout6->addStretch();

    qhboxlayout6->addWidget(m_clean);

    //

    QVBoxLayout *mainlayout = new QVBoxLayout;

    mainlayout->addLayout(qhboxlayout1,1);

    mainlayout->addLayout(qhboxlayout2,0.5);

    mainlayout->addLayout(qhboxlayout3,1);

    mainlayout->addLayout(qhboxlayout4,0.5);

    mainlayout->addLayout(qhboxlayout5,3);

    mainlayout->addLayout(qhboxlayout6,1);

    setLayout(mainlayout);

    setWindowTitle("Websocket Client");

    this->setFixedSize(800, 600); // 窗口固定为800x600像素,无法缩放

    connect(m_linkbutton,SIGNAL(clicked(bool)),this,SLOT(connectToServer()));

    connect(m_disconnectbutton,SIGNAL(clicked(bool)),this,SLOT(stopClicked()));

    connect(m_sendbutton,SIGNAL(clicked(bool)),this,SLOT(onSendButtonClicked()));

    connect(m_clean,SIGNAL(clicked(bool)),this,SLOT(onCleanButtonClicked()));

    connect(&m_websocket,SIGNAL(connected()),this,SLOT(onconnected()));

    connect(&m_websocket,SIGNAL(disconnected()),this,SLOT(closeConnection()));

    connect(&m_websocket,SIGNAL(textMessageReceived(QString)),this,SLOT(onTextMessageReceived(QString)));

}

ClientDialog::~ClientDialog()

{

    m_websocket.errorString();

    m_websocket.close();

}

//断开连接操作

void ClientDialog::closeConnection(){

    m_linkbutton->setEnabled(true);

    m_disconnectbutton->setEnabled(false);

    m_sendmessagetextedit->setEnabled(false);

    m_sendbutton->setEnabled(false);

    m_receivemessageTextEdit->setEnabled(false);

    m_clean->setEnabled(false);

    statusLabel->setText(tr("disconnected"));

}

//连接服务器

void ClientDialog::connectToServer()

{

    QString path = QString("ws://%1:%2").arg(m_iplineedit->text()).arg(m_portspinbox->text());

    QUrl url = QUrl(path);

    m_websocket.open(url);

}

//连接上之后

void ClientDialog::onconnected(){

    qDebug() << "hello word!";

    statusLabel->setText(tr("connected"));

    m_linkbutton->setEnabled(false);

    m_disconnectbutton->setEnabled(true);

    m_sendmessagetextedit->setEnabled(true);

    m_sendbutton->setEnabled(true);

    m_receivemessageTextEdit->setEnabled(true);

    m_clean->setEnabled(true);

}

//收到消息

void ClientDialog::onTextMessageReceived(const QString &message)

{

    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");

    m_receivemessageTextEdit->setText(time + "\r\n" + "message: " + message);

}

//断开

void ClientDialog::stopClicked()

{

    m_websocket.close();

}

//发送消息

void ClientDialog::onSendButtonClicked()

{

    QString msg= m_sendmessagetextedit->document()->toPlainText();

    m_websocket.sendTextMessage(msg);

}

//清除内容

void ClientDialog::onCleanButtonClicked()

{

    m_receivemessageTextEdit->clear();

}

2.服务端代码

#include "webserver.h"

#include "ui_webserver.h"

#include <QtCore/QVariant>

#include <QtWidgets/QApplication>

#include <QtWidgets/QButtonGroup>

#include <QtWidgets/QHeaderView>

#include <QtWidgets/QLabel>

#include <QtWidgets/QListWidget>

#include <QtWidgets/QPushButton>

#include <QtWidgets/QSpinBox>

#include <QtWidgets/QTextBrowser>

#include <QtWidgets/QWidget>

#include <QHBoxLayout>

webserver::webserver(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::webserver)

{

    ui->setupUi(this);

    {

         //qhboxLayout1部分代码

         QLabel* monitorLabel= new QLabel("监听端口");

         m_monitorSpinBox = new QSpinBox();

         m_monitorSpinBox->setRange(0,65535);

         m_monitorSpinBox->setValue(5123);

         m_startButton = new QPushButton("启动服务");

         m_stopButton = new QPushButton("停止服务");

         m_stopButton->setEnabled(false);

         QHBoxLayout *qhboxLayout = new QHBoxLayout;

         qhboxLayout->addWidget(monitorLabel);

         qhboxLayout->addWidget(m_monitorSpinBox);

         qhboxLayout->addWidget(m_startButton);

         qhboxLayout->addWidget(m_stopButton);

        //qhboxLayout2部分代码

          QLabel* sendLabel=new QLabel("发送消息");

          sendLabel->setFixedHeight(30);

          QHBoxLayout *qhboxLayout2 = new QHBoxLayout;

          qhboxLayout2->addWidget(sendLabel);

        //qhboxLayout3部分代码

          m_sendTextedit = new QTextEdit;

          m_sendTextedit = new QTextEdit();

          m_sendTextedit->setEnabled(false);

          m_sendTextedit->setFixedHeight(80);

          m_sendButton= new QPushButton("发送");

          m_sendButton->setEnabled(false);

          m_sendButton->setFixedHeight(50);

          QHBoxLayout *qhboxlayout3 = new QHBoxLayout;

          qhboxlayout3->addWidget(m_sendTextedit,2);

          qhboxlayout3->addWidget(m_sendButton,1);

        //qvboxlayout411

          QLabel* receiveLabel = new QLabel("接收消息");

          receiveLabel->setFixedHeight(30);

          m_receiveTextEdit = new QTextEdit();

          m_receiveTextEdit->setReadOnly(true);

          QVBoxLayout *qvboxlayout411 = new QVBoxLayout;

          qvboxlayout411->addWidget(receiveLabel);

          qvboxlayout411->addWidget(m_receiveTextEdit);

            //qhboxlayout412

          m_cleanButton = new QPushButton("清除");

          m_cleanButton->setEnabled(false);

          QHBoxLayout *qhboxlayout412 = new QHBoxLayout;

          qhboxlayout412->addStretch();

          qhboxlayout412->addWidget(m_cleanButton);

        //qvboxlayout41

          QVBoxLayout *qvboxlayout41 = new QVBoxLayout;

          qvboxlayout41->addLayout(qvboxlayout411);

          qvboxlayout41->addLayout(qhboxlayout412);

        //qvboxlayout42

          QLabel* linkclientLabel=new QLabel("连接客户端");

          linkclientLabel->setFixedHeight(30);

          m_linkclientListWidget=new QListWidget;

          QVBoxLayout* qvboxlayout42 = new QVBoxLayout;

          qvboxlayout42->addWidget(linkclientLabel);

          qvboxlayout42->addWidget(m_linkclientListWidget);

        //qvboxlayout4

          QHBoxLayout *qhboxlayout4 = new QHBoxLayout;

          qhboxlayout4->addLayout(qvboxlayout41,2);

          qhboxlayout4->addLayout(qvboxlayout42,1);

         //mainlayout

          QVBoxLayout *mainLayout = new QVBoxLayout;

          mainLayout->addLayout(qhboxLayout,1);

          mainLayout->addLayout(qhboxLayout2,1);

          mainLayout->addLayout(qhboxlayout3,1);

          mainLayout->addLayout(qhboxlayout4,3);

          this->setLayout(mainLayout);

          this->setWindowTitle("Websocket Server -- Neo");

          this->setFixedSize(800, 600); // 窗口固定为800x600像素,无法缩放

    }

    m_WebSocketServer=new QWebSocketServer("server",QWebSocketServer::NonSecureMode);

    connect(m_WebSocketServer,SIGNAL(newConnection()),this,SLOT(onNewConnection()));

    connect(m_WebSocketServer, SIGNAL(closed()), this, SLOT(onClosed()));

    connect(m_WebSocketServer, SIGNAL(serverError(QWebSocketProtocol::CloseCode)),

            this, SLOT(onServerError(QWebSocketProtocol::CloseCode)));

    connect(m_startButton,SIGNAL(clicked(bool)),this,SLOT(onStartButtonClick()));

    connect(m_stopButton,SIGNAL(clicked(bool)),this,SLOT(onStopButtonClick()));

    connect(m_cleanButton,SIGNAL(clicked(bool)),this,SLOT(onCleanButtonClick()));

    connect(m_sendButton,SIGNAL(clicked(bool)),this,SLOT(onSendButtonClick()));

}

webserver::~webserver()

{

    if(m_WebSocketServer)

    {

        m_WebSocketServer->close();

    }

    delete ui;

}

//开启服务

void webserver::onStartButtonClick(){

    int i_port = m_monitorSpinBox->text().toInt();

    m_WebSocketServer->listen(QHostAddress::Any,i_port);

    m_startButton->setEnabled(false);

    m_stopButton->setEnabled(true);

    qDebug()<<m_WebSocketServer->isListening();

    qDebug()<<m_WebSocketServer->serverPort();

    qDebug()<<m_WebSocketServer->serverAddress();

}

//停止服务

void webserver::onStopButtonClick(){

    m_startButton->setEnabled(true);

    m_stopButton->setEnabled(false);

    m_WebSocketServer->close();

}

//发送信息

void webserver::onSendButtonClick(){

    QString msg = m_sendTextedit->document()->toPlainText();

    int currenRow = m_linkclientListWidget->currentRow();//当前单击选中ListWidget控件的行号

    if(currenRow==-1)

    {

        currenRow = 0;

    }

    QString key = m_linkclientListWidget->item(currenRow)->text();

    if(_hashIpPort2PWebSocket.contains(key))

    {

        _hashIpPort2PWebSocket.value(key)->sendTextMessage(msg);

    }

}

void webserver::onCleanButtonClick(){

    m_receiveTextEdit->clear();

}

//连接上之后

void webserver::onNewConnection(){

    qDebug() << "connect ok";

    m_startButton->setEnabled(false);

    m_stopButton->setEnabled(true);

    m_sendTextedit->setEnabled(true);

    m_sendButton->setEnabled(true);

    m_cleanButton->setEnabled(true);

    QWebSocket *pWebSocket = m_WebSocketServer->nextPendingConnection();

    connect(pWebSocket,SIGNAL(textMessageReceived(QString)),this,SLOT(slot_processTextMessage(QString)));

    connect(pWebSocket,SIGNAL(disconnected()),this,SLOT(slot_socketDisconnected()));

    connect(pWebSocket, SIGNAL(error(QAbstractSocket::SocketError)),

            this      , SLOT(slot_error(QAbstractSocket::SocketError)));

    quint32 ipv4Address =  pWebSocket->peerAddress().toIPv4Address();

    QString ipString = QHostAddress(ipv4Address).toString();

    _hashIpPort2PWebSocket.insert(QString("%1-%2").arg(ipString).arg(pWebSocket->peerPort()),pWebSocket);

    QString item = QString("%1-%2").arg(ipString).arg(pWebSocket->peerPort());

    m_linkclientListWidget->addItem(item);

}

//停止之后

void webserver::onClosed()

{

    QList<QWebSocket *> _listWebSocket = _hashIpPort2PWebSocket.values();

    for(int index = 0; index < _listWebSocket.size(); index++)

    {

        _listWebSocket.at(index)->close();

    }

    _hashIpPort2PWebSocket.clear();

    m_linkclientListWidget->clear();

}

//连接错误

void webserver::onServerError(QWebSocketProtocol::CloseCode closeCode)

{

    QWebSocket *pWebSocket = dynamic_cast<QWebSocket *>(sender());

    if(!pWebSocket)

    {

        return;

    }

}

void webserver::slot_error(QAbstractSocket::SocketError error)

{

    QWebSocket *pWebSocket = dynamic_cast<QWebSocket *>(sender());

    if(!pWebSocket)

    {

        return;

    }

}

//收到消息并显示

void webserver::slot_processTextMessage(QString message){

    QWebSocket *pWebSocket = dynamic_cast<QWebSocket *>(sender());

    if(!pWebSocket)

    {

        return;

    }

    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");

    quint32 ipv4Address =  pWebSocket->peerAddress().toIPv4Address();

    QString ipString = QHostAddress(ipv4Address).toString();

    QString item = QString("IP: %1, port: %2").arg(ipString).arg(pWebSocket->peerPort());

    m_receiveTextEdit->append(time+ "\r\n" + item + "\r\n" + "message: " + message + "\r\n");

    //qDebug()<< pWebSocket->peerAddress().toString();

    //qDebug() << "Client IP address: " << ipString;

}

//连接断开的操作

void webserver::slot_socketDisconnected(){

    QWebSocket *pWebSocket = dynamic_cast<QWebSocket *>(sender());

    if(!pWebSocket)

    {

        return;

    }

    //qDebug() << __FILE__ << __LINE__ << __FUNCTION__;

    quint32 ipv4Address =  pWebSocket->peerAddress().toIPv4Address();

    QString ipString = QHostAddress(ipv4Address).toString();

    QString item1 = QString("%1-%2").arg(ipString).arg(pWebSocket->peerPort());

    _hashIpPort2PWebSocket.remove(QString("%1-%2").arg(ipString).arg(pWebSocket->peerPort()));

    QListWidgetItem *item3;

    for(int i=0;i<m_linkclientListWidget->count();i++)

    {

        QString str = m_linkclientListWidget->item(i)->text();

        if(str == item1)

        {

            item3 = m_linkclientListWidget->takeItem(i);

            m_linkclientListWidget->removeItemWidget(item3);

            delete item3;

        }

    }

}

void webserver::on_m_linkclientListWidget_clicked(const QModelIndex &index)

{

int currenRow = m_linkclientListWidget->currentRow();

}

3.完整工程代码下载

https://download.csdn.net/download/xieliru/90787178


网站公告

今日签到

点亮在社区的每一天
去签到