当前位置: 首页 > news >正文

Qt WebSocket

简介

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();}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 10款主流图纸加密软件大盘点(为图纸穿上隐形衣)
  • 幅频特性曲线分析及使用WPF绘制
  • 动手学深度学习7.7. 稠密连接网络(DenseNet)-笔记练习(PyTorch)
  • 供应链系统源码的关键技术是什么?
  • RTC碰到LXTAL低频晶振停振怎么办?
  • docker的前端部署1
  • 构建基于I2C与UART通信的智能嵌入式机械臂抓取系统,结合OpenCV技术进行高效物体识别与动作控制的综合解决方案(代码示例)
  • BaseCTF-web-Week1
  • shell脚本-采集容器内自定义端口tcp连接数并通过http接口推送到Prometheus
  • Qt中英文支持
  • Openresty 中 ngx.exit(403)的时候,给403页面的body中传递一个参数展示出来
  • 怎样快速搭建 Linux 虚拟机呢?(vagrant 篇)
  • unity3d拼图__附带资源
  • 上书房信息咨询:消费者需求研究指标设计
  • 医院挂号系统的伪代码
  • 2017-08-04 前端日报
  • eclipse(luna)创建web工程
  • IDEA常用插件整理
  • javascript 哈希表
  • javascript从右向左截取指定位数字符的3种方法
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Sass Day-01
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 用 Swift 编写面向协议的视图
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • ​iOS实时查看App运行日志
  • # 飞书APP集成平台-数字化落地
  • #职场发展#其他
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • $().each和$.each的区别
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (层次遍历)104. 二叉树的最大深度
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (附源码)ssm码农论坛 毕业设计 231126
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (四)stm32之通信协议
  • (一)u-boot-nand.bin的下载
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (原创)可支持最大高度的NestedScrollView
  • **PHP二维数组遍历时同时赋值
  • .htaccess配置重写url引擎
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET 4.0中的泛型协变和反变
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .net Signalr 使用笔记
  • .Net(C#)自定义WinForm控件之小结篇
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • /3GB和/USERVA开关
  • [001-03-007].第07节:Redis中的事务
  • [23] GaussianAvatars: Photorealistic Head Avatars with Rigged 3D Gaussians
  • [ABP实战开源项目]---ABP实时服务-通知系统.发布模式
  • [AIGC] Java List接口详解
  • [Android]How to use FFmpeg to decode Android f...