Qt WebSocket

server/2024/9/24 8:54:44/
简介

WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。允许服务端主动向客户端推送数据

在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程。

默认情况下:WebSocket 协议使用 80 端口;

WebSocket目前支持两种统一资源标志符ws和wss,类似于HTTP和HTTPS。

如:  ws://example.com:80/some/path

如:  ws://127.0.0.1:45678    即ip+端口

产生背景:

因为 HTTP 协议有一个缺陷:通信只能由客户端发起。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开), 因此websocket应运而生。

优点:

1)较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小;

2)更强的实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少;

3)保持连接状态:与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息;

4)更好的二进制支持:WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容;

5)可以支持扩展:WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。

QWebSocket

要使用 Qt 的 WebSocket 模块,先在 pro 文件中加上 websockets.

QT += websockets

QWebSocket
常用函数

QHostAddress localAddress() const;

quint16 localPort() const;

QUrl requestUrl() const;

QNetworkRequest request() const;

当有需要接收的数据时,会发出该信号

void textMessageReceived(const QString &message);  //字符串的方式接收数据信息

void binaryMessageReceived(const QByteArray &message);//二进制的方式接收数据信息

当需要发送数据时,调用下面的发送函数

qint64 sendTextMessage(const QString &message); //字符串的方式发送数据信息

qint64 sendBinaryMessage(const QByteArray &data);//二进制的方式发送数据信息

客户端断开连接,收到断开信号:

void disconnected();

服务端QWebSocketServer

QWebSocketServer以 QTcpServer 为模型,并且行为相同。所以,如果你知道如何使用 QTcpServer,你就知道如何使用 QWebSocketServer.

常用的函数、信号、槽函数:
  1. bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);

监听连接。 如果端口为 0,则自动选择一个端口。 如果地址是 QHostAddress::Any,则服务器将侦听所有网络接口。

  1. void newConnection();  信号,接收连接,发出该信号
  2. virtual QWebSocket *nextPendingConnection();  //获取连接的客户端
  3. void close();    //关闭监听套接字

流程
  1. 创建服务器:new QWebSocketServer
  2. 监听:listen

m_pWebSocketServer->listen(QHostAddress::LocalHost, mPort);//端口号

  1. 有新的连接,触发这个信号:QWebSocketServer::newConnection
  2. 在处理newConnection信号的槽函数中,获得新的QWebSocket客户端:QWebSocketServer::nextPendingConnection
  3. 接收到信息时候,触发信号:QWebSocket::binaryMessageReceived或者QWebSocket::textMessageReceived
  4. 发送数据,调用函数QWebSocket::sendTextMessage或QWebSocket::sendBinaryMessage
  5. 客户端断开连接,触发信号:QWebSocket::disconnected,在处理disconnected信号的槽函数中,QWebSocket客户端调用deleteLater,进行释放资源。
  6. 主动关闭服务端,调用QWebSocketServer::close,所有的客户端连接qDeleteAll(m_clients.begin(), m_clients.end());

客户端QWebSocket

客户端直接使用QWebSocket的函数进行操作即可

流程
  1. 创建客户端:new QWebSocket
  2. 通过QWebSocket的open 函数连接服务端的 Url
  3. 连接服务端成功后,会触发QWebSocket::connected信号。从而获知连接成功
  4. 发送数据,调用函数QWebSocket::sendTextMessage或QWebSocket::sendBinaryMessage
  5. 接收到信息时候,触发信号:QWebSocket::binaryMessageReceived或者QWebSocket::textMessageReceived
  6. 客户端关闭时,要主动调QWebSocket的close函数,让服务端知道该客户端已断开连接。
  7. 断开连接成功,会触发信号QWebSocket::disconnected,在该信号的槽函数处理断开连接的相应工作。

完整代码

开发环境:QT5.15.2 MSVC 2019 64Bit

服务端
#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include <QWebSocketServer>#include <QWebSocket>#include <QMap>QT_BEGIN_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:void sendTextMessageSignal(QString msg);private slots:void on_pushButton_Listen_clicked();void OnNewConnectionSlot();void OnTextReceivedSlot(QString msg);void OndisconnectedSlot();void on_pushButton_send_clicked();private:Ui::Widget *ui;QWebSocketServer *server;QList<QWebSocket*> m_clientList;QMap<QWebSocket*,QString> m_clientStatus;};#endif // WIDGET_H#include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);server = new QWebSocketServer("testserver",QWebSocketServer::NonSecureMode,this);connect(server,&QWebSocketServer::newConnection,this,&Widget::OnNewConnectionSlot);ui->lineEdit_address->setText("Any");ui->lineEdit_port->setText("45678");}Widget::~Widget(){qDeleteAll(m_clientList);server->close();delete ui;}void Widget::on_pushButton_Listen_clicked(){QHostAddress address;if(ui->lineEdit_address->text()=="Any"){address=QHostAddress::Any;}else{address=QHostAddress(ui->lineEdit_address->text());}int nPort = ui->lineEdit_port->text().toInt();bool bListen = server->listen(address,nPort);if(bListen)ui->pushButton_Listen->setEnabled(false);}void Widget::OnNewConnectionSlot(){QWebSocket *client = server->nextPendingConnection();m_clientList.append(client);qDebug() << "Client:" << client->peerAddress().toString() << client->peerName() <<client->peerPort() ;QString strstatus = "ip:" + client->peerAddress().toString() + " 端口:"+  QString::number(client->peerPort()) + "连接成功\n";m_clientStatus.insert(client,strstatus);ui->textEdit_clientlist->insertPlainText(strstatus);ui->textEdit_clientlist->connect(client,&QWebSocket::textMessageReceived,this,&Widget::OnTextReceivedSlot);connect(this,&Widget::sendTextMessageSignal,client,&QWebSocket::sendTextMessage);//给所有客户端发送数据connect(client,&QWebSocket::disconnected,this,&Widget::OndisconnectedSlot);}void Widget::OnTextReceivedSlot(QString msg){ui->textEdit_recv->setText(msg);QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());pClient->sendTextMessage("回答" + msg);}void Widget::OndisconnectedSlot(){QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());m_clientStatus.remove(pClient);ui->textEdit_clientlist->clear();QList<QString> textlist = m_clientStatus.values();for(auto text : textlist){ui->textEdit_clientlist->insertPlainText(text);}m_clientList.removeAll(pClient);pClient->deleteLater();}void Widget::on_pushButton_send_clicked(){QString msg = ui->textEdit_send->toPlainText();emit sendTextMessageSignal(msg);}

客户端
#ifndef WIDGET_H#define WIDGET_H#include <QWidget>#include <QWebSocket>QT_BEGIN_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_connect_clicked();void onConnectedSlot();void ondisconnectedSlot();void onTextReceivedSlot(QString message);void on_pushButton_send_clicked();void on_pushButton_disconnect_clicked();private:Ui::Widget *ui;QWebSocket *m_ClientSocket;};#endif // WIDGET_H#include "widget.h"#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);m_ClientSocket = new QWebSocket();m_ClientSocket->setParent(this);ui->label_connet->setText("尚未连接服务端");ui->pushButton_connect->setEnabled(true);ui->pushButton_disconnect->setEnabled(false);ui->lineEdit_url->setText("ws://127.0.0.1:45678");}Widget::~Widget(){m_ClientSocket->close();delete ui;}void Widget::on_pushButton_connect_clicked(){QUrl url(ui->lineEdit_url->text());m_ClientSocket->open(url);connect(m_ClientSocket, &QWebSocket::connected, this, &Widget::onConnectedSlot);connect(m_ClientSocket, &QWebSocket::disconnected, this, &Widget::ondisconnectedSlot);}void Widget::onConnectedSlot(){ui->pushButton_connect->setEnabled(false);ui->pushButton_disconnect->setEnabled(true);QString strstatus =  "连接服务端" +  ui->lineEdit_url->text() + "成功";ui->label_connet->setText(strstatus);connect(m_ClientSocket, &QWebSocket::textMessageReceived, this, &Widget::onTextReceivedSlot);}void Widget::ondisconnectedSlot(){QString strstatus =  "断开服务端" +  ui->lineEdit_url->text() + "连接";ui->label_connet->setText(strstatus);ui->pushButton_connect->setEnabled(true);ui->pushButton_disconnect->setEnabled(false);}void Widget::onTextReceivedSlot(QString message){ui->textEdit_recv->setText(message);}void Widget::on_pushButton_send_clicked(){QString message = ui->textEdit_send->toPlainText();m_ClientSocket->sendTextMessage(message);}void Widget::on_pushButton_disconnect_clicked(){m_ClientSocket->close();}


http://www.ppmy.cn/server/106245.html

相关文章

【Java】—— Java面向对象基础:Person类实例操作

目录 一、定义Person类 二、创建Person对象并操作 三、理解对象之间的关系 四、总结 在Java编程中&#xff0c;面向对象编程&#xff08;OOP&#xff09;是一种非常核心且广泛使用的编程范式。它允许我们通过类&#xff08;Class&#xff09;来定义对象的属性和行为&#x…

面试题目:(6)翻转二叉树

题目 翻转二叉树 &#xff08;中间对称翻转&#xff0c;等于镜像&#xff09;给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。示例1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9]输出&#xff1a;[4,7,2,9,6,3,1]示例1&#xff1…

Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机本身的数据保存(CustomData)功能(Python)

Baumer工业相机堡盟工业相机如何通过BGAPI SDK设置相机本身的数据保存&#xff08;CustomData&#xff09;功能&#xff08;Python&#xff09; Baumer工业相机Baumer工业相机的数据保存&#xff08;CustomData&#xff09;功能的技术背景CameraExplorer如何使用数据保存&#…

【C#】【EXCEL】Bumblebee/Classes/ExRange.cs

Flow diagram 为了创建一个全面但不过于复杂的流程图&#xff0c;我们将重点关注 ExRange 类的主要功能和方法。以下是一个中英双语的流程图&#xff0c;展示了 ExRange 类的主要结构和操作流程&#xff1a; #mermaid-svg-SYKM6gZKQKi0qEx1 {font-family:"trebuchet ms&q…

Django Signals

Django Signals 当某个事件发生的时候&#xff0c;signal(信号)允许senders(发送者)用来通知receivers(接收者)&#xff0c;通知receivers干嘛&#xff1f;你想要recivers干嘛就可以干嘛。这在多处代码对同一个事件感兴趣的时候就有用武之地了。 比如&#xff1a;Django提供了…

找在两个汉字中间的句号,如何写正则表达式?

要在两个汉字之间找到句号&#xff0c;可以使用以下正则表达式&#xff1a; (?<[\u4e00-\u9fff])。(?[\u4e00-\u9fff])解释&#xff1a; (?<[\u4e00-\u9fff]) 表示前面的字符是汉字。。 表示句号。(?[\u4e00-\u9fff]) 表示后面的字符是汉字。 这个正则表达式将匹配…

爆改YOLOv8 | 利用MB-TaylorFormer提高YOLOv8图像去雾检测

1&#xff0c;本文介绍 MB-TaylorFormer是一种新型多支路线性Transformer网络&#xff0c;用于图像去雾任务。它通过泰勒展开改进了softmax-attention&#xff0c;使用多支路和多尺度结构以获取多层次和多尺度的信息&#xff0c;且比传统方法在性能、计算量和网络重量上更优。…

Git的使用教程及常用语法02

四.将文件添加到仓库 创建仓库 git init查看仓库的状态 git status 添加到暂存区 git add提交 git commitgit status 可以查看当前仓库的状态信息&#xff0c;例如包含哪些分支&#xff0c;有哪些文件以及这些文件当前处在怎样的一个状态。 由于当前没有存储任何的东西&…