Qt 5.14.2 学习记录 —— 이십일 Qt网络和音频

ops/2025/2/3 4:56:09/

文章目录

  • 1、UDP
    • 带有界面的Udp服务器(回显服务器)
  • 2、TCP
    • 回显服务器
  • 3、HTTP客户端
  • 4、音频


和Linux的网络一样,Qt封装了Linux的网络API,即Socket API。网络编程是在应用层写,需要传输层支持,传输层有UDP和TCP,Qt对此有两套API。

Qt网络编程时需要在.pro文件添加network模块。

1、UDP

两个类QUdpSocket,QNetworkDatagram,后者就是数据报。

QUdpSocket

在这里插入图片描述

不过Qt不是用堵塞的,而是利用信号槽。当socket收到请求时,QUdpSocket就会触发这个readyRead信号,此时槽函数就能进行处理逻辑了,整体也就形成了事件驱动的网络编程。

QNetworkDatagram

在这里插入图片描述

带有界面的Udp服务器(回显服务器)

创建一个QWidget项目,放一个List Widget到界面中。

pro文件里

QT       += core gui network

服务端

// widget.h#include <QWidget>
#include <QUdpSocket>
#include <QNetworkDatagram>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;QUdpSocket* socket;void processRequest();QString process(const QString& request);
};// widget.cpp#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 加上this就挂上了对象树, 自动析构// 不加就在析构函数里写上delete socketsocket = new QUdpSocket(this);this->setWindowTitle("服务器");// 先连接信号槽再绑定端口号// 绑定好端口号那么请求就可以收到了connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);// Any表示任何IP地址都可以识别到bool flag = socket->bind(QHostAddress::Any, 9090);if (!flag){QMessageBox::critical(this, "服务器启动出错", socket->errorString());return ;}
}Widget::~Widget()
{delete ui;
}void Widget::processRequest()
{// 读取请求并解析const QNetworkDatagram& requestDatagram = socket->receiveDatagram();QString request = requestDatagram.data();// data()返回一个QByteArray对象, 这个类型可以赋值给QString// 根据请求计算响应 (由于是回显服务器, 响应就是请求自身, 不需要计算)const QString& response = process(request);// 响应写回给客户端// toUtf8()取出QString内部的字节数组// 后面两个参数表明要发送到哪个主机的哪个端口号QNetworkDatagram responseDatagram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());// 发送socket->writeDatagram(responseDatagram);// 交互的信息显示到界面上QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort()) + "] req: " + request + ", resp: " + response;ui->listWidget->addItem(log);
}QString Widget::process(const QString &request)
{// 回显服务器中响应和请求完全一样return request;
}

客户端,在同一目录下再创建一个QWidget项目即可。界面

在这里插入图片描述

如何做出这个界面就不写了,重点在客户端代码上

// widget.h#include <QWidget>
#include <QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();void processResponse();private:Ui::Widget *ui;QUdpSocket* socket;
};// widget.cpp#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QNetworkDatagram>const QString& SERVER_IP = "127.0.0.1";
// 端口号本质是一个2字节的无符号整数
// quint16是Qt的unsigned short, 其大小是2个字节
const quint16 SERVER_PORT = 9090;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);socket = new QUdpSocket(this);this->setWindowTitle("客户端");// 处理服务器返回的数据connect(socket, &QUdpSocket::readyRead, this, &Widget::processResponse);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{// 获取输入框内容const QString& text = ui->lineEdit->text();// 构造UDP请求数据QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);// 发送请求数据socket->writeDatagram(requestDatagram);// 发送的请求添加到列表框中ui->listWidget->addItem("客户端内容: " + text);// 输入框清空ui->lineEdit->setText("");
}void Widget::processResponse()
{// 读取响应const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();// 显示ui->listWidget->addItem("服务器响应: " + response);
}

2、TCP

QTcpServer

在这里插入图片描述
在这里插入图片描述

QTcpScket

在这里插入图片描述

回显服务器

服务端,放一个List Widget到界面上

// widget.h#include <QWidget>
#include <QTcpServer>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void processConnection();QString process(const QString request);private:Ui::Widget *ui;QTcpServer* tcpServer;
};// widget.cpp#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QTcpSocket>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("服务器");//创建QTcpServer实例tcpServer = new QTcpServer(this);// 接收响应的处理函数connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);// 绑定并监听端口号bool flag = tcpServer->listen(QHostAddress::Any, 9090);if (!flag){QMessageBox::critical(this, "服务器启动失败!", tcpServer->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::processConnection()
{// 通过tcpServer拿到socket对象, 用于和客户端进行通信QTcpSocket* clientSocket = tcpServer->nextPendingConnection();QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端上线!";ui->listWidget->addItem(log);// 处理客户端的请求connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {// 返回的是QByteArray, 通过赋值转为QStringQString request = clientSocket->readAll();// 由于是回显服务器, 请求就是响应const QString& response = process(request);clientSocket->write(response.toUtf8());QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] " + " req: " + request + ", resp: " + response;ui->listWidget->addItem(log);});// 客户端断开连接connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";ui->listWidget->addItem(log);// 释放clientSocket, 防止文件描述符泄漏// 非立即销毁, 而是通知Qt在下一个事件循环中销毁clientSocket->deleteLater();});
}QString Widget::process(const QString request)
{return request;
}

客户端

在这里插入图片描述

// widget.h#include <QWidget>
#include <QTcpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QTcpSocket* socket;
};// widget.cpp#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客户端");socket = new QTcpSocket(this);// 和服务器建立连接// 这个函数非阻塞socket->connectToHost("127.0.0.1", 9090);connect(socket, &QTcpSocket::readyRead, this, [=]() {QString response = socket->readAll();ui->listWidget->addItem("服务器内容: " + response);});// 阻塞等待, 确认连接成功bool flag = socket->waitForConnected();if (!flag){QMessageBox::critical(this, "连接服务器失败", socket->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{const QString& text = ui->lineEdit->text();// 发送数据给服务器socket->write(text.toUtf8());// 显示ui->listWidget->addItem("客户端内容: " + text);// 清空输入框ui->lineEdit->setText("");
}

3、HTTP客户端

Qt只提供HTTP客户端。核心API:QNetworkAccessManager , QNetworkRequest , QNetworkReply。

QNetworkAccessManager

在这里插入图片描述

发起HTTP GET和POST请求,皆返回QNetworkReply 对象。

QNetworkRequest表示一个HTTP请求协议,不包含body。如果需要发送一个带有 body 的请求(比如 post),会在 QNetworkAccessManager 的 post 方法中通过单独的参数来传入body。

在这里插入图片描述

QVariant是一个类型可变的值。

QNetworkRequest::KnownHeaders 是一个枚举类型,常用取值:

在这里插入图片描述
在这里插入图片描述

QNetworkReply 表示一个 HTTP 响应。这个类同时也是 QIODevice 的子类。

在这里插入图片描述

QNetworkReply 信号 finished 会在客户端收到完整的响应数据之后触发。

在这里插入图片描述
在这里插入图片描述

// widget.h#include <QWidget>
#include <QNetworkAccessManager>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QNetworkAccessManager* manager;
};// widget.cpp#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QNetworkReply>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客户端");manager = new QNetworkAccessManager(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{// 获取输入框的urlQUrl url(ui->lineEdit->text());// 构造HTTP请求对象QNetworkRequest request(url);// 发送请求// 如果是post, 第二个参数就是body// get不阻塞, 因为它不负责等待响应QNetworkReply* response = manager->get(request);// 信号槽处理响应connect(response, &QNetworkReply::finished, this, [=]() {if(response->error() == QNetworkReply::NoError){QString html = response->readAll();ui->plainTextEdit->setPlainText(html);}elseui->plainTextEdit->setPlainText(response->errorString());response->deleteLater();});
}

4、音频

关于Qt的音视频,需要先引入multimedia多媒体模块才能正常运行。主要的类是QSound,只能播放.wav格式的音频文件

创建一个QWidget项目,使用qrc来引入音频文件

在这里插入图片描述

放一个按钮到界面上

// widget.h#include <QWidget>
#include <QSound>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QSound* sound;
};// widget.cpp#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);sound = new QSound(":/audio.wav", this);
}void Widget::on_pushButton_clicked()
{sound->play();
}

结束。


http://www.ppmy.cn/ops/155226.html

相关文章

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体&#xff1f; 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…

C++ 字母大小写转换两种方法统计数字字符的个数

目录 题目&#xff1a; 代码1&#xff1a; 代码2&#xff1a; 题目描述输入一行字符&#xff0c;统计出其中数字字符的个数。 代码如下&#xff1a; 判断⼀个字符是否是数字字符有⼀个函数是 isdigit ,可以直接使⽤。 代码如下&#xff1a; 题目&#xff1a; 大家都知道…

UbuntuWindows双系统安装

做系统盘&#xff1a; Ubuntu20.04双系统安装详解&#xff08;内容详细&#xff0c;一文通关&#xff01;&#xff09;_ubuntu 20.04-CSDN博客 ubuntu系统调整大小&#xff1a; 调整指南&#xff1a; 虚拟机中的Ubuntu扩容及重新分区方法_ubuntu重新分配磁盘空间-CSDN博客 …

css设置盒子动画,CSS3 transition动画 animation动画

CSS3 transition动画 transition-property 设置过渡的属性&#xff0c;比如&#xff1a;width height background-color transition-duration 设置过渡的时间&#xff0c;比如&#xff1a;1s 500ms transition-timing-function 设置过渡的运动方式 linear 匀速 ease 开始和…

ASP.NET Core自定义 MIME 类型配置

自定义MIME类型 上篇文章讲了 ASP.NET Core 启动并提供静态文件&#xff0c;如果需要自定义MIME类型&#xff0c;也可以通过.NET Core 配置来实现 &#xff0c;例如下边代码&#xff1a; 默认情况下&#xff0c;ASP.NET Core 对于常见的文件扩展名&#xff08;如 .jpg、.png、.…

深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile

深入理解 C 语言函数指针的高级用法 函数指针是 C 语言中极具威力的特性&#xff0c;广泛用于实现回调、动态函数调用以及灵活的程序设计。然而&#xff0c;复杂的函数指针声明常常让即使是有经验的开发者也感到困惑。本文将从函数指针的基本概念出发&#xff0c;逐步解析复杂…

为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️

前言 在使用 Spring 框架时&#xff0c;依赖注入&#xff08;DI&#xff09;是一个非常重要的概念。通过注解&#xff0c;我们可以方便地将类的实例注入到其他类中&#xff0c;提升开发效率。Autowired又是被大家最为熟知的方式&#xff0c;但很多开发者在使用 IntelliJ IDEA …

水稻和杂草检测数据集VOC+YOLO格式1356张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1356 标注数量(xml文件个数)&#xff1a;1356 标注数量(txt文件个数)&#xff1a;1356 标注…