欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:QT
目录
- 👉🏻QThead
- 👉🏻QMutex
- 👉🏻QWaitCondition
- 👉🏻QUdpSocket
- 👉🏻QNetworkDatagram
- 👉🏻Udp实现客户端和服务端回显
- [👉🏻QTcpServer 和 QTcpSocket](#👉🏻QTcpServer 和 QTcpSocket)
- 👉🏻HTTP
- 👉🏻音视频
👉🏻QThead
QThread是Qt框架中用于多线程编程的基类,它提供了创建和管理线程的能力。以下是QThread的一些常用API:
run()
根据官方文档,我们需要重写run函数
- 创建 QThread 的子类
通常,你会通过继承 QThread
并重写其 run()
方法来创建自定义的线程类。run()
方法包含了线程将要执行的代码。
cpp
#include <QThread>
class WorkerThread : public QThread
{
Q_OBJECT
public:
void run() override {
// 在这里编写线程要执行的代码
// 例如,一个耗时的循环
for (int i = 0; i < 5; ++i) {
QThread::sleep(1); // 模拟耗时操作
qDebug() << "Working in thread" << QThread::currentThreadId();
}
}
};
- 创建并启动线程
然后,你可以创建 WorkerThread
的实例,并调用其 start()
方法来启动线程。start()
方法会自动调用 run()
方法。
cpp
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerThread *worker = new WorkerThread();
worker->start(); // 启动线程
// 主线程可以继续执行其他任务
qDebug() << "Working in main thread" << QThread::currentThreadId();
// 等待线程完成
worker->wait();
delete worker;
return a.exec();
}
👉🏻QMutex
QMutex
是 Qt 框架中用于管理对共享资源的互斥访问的一个类。在多线程程序中,多个线程可能会尝试同时访问同一个资源(如内存中的变量、文件句柄等),这可能导致数据损坏或不一致。QMutex
提供了一种机制来确保在同一时间内只有一个线程可以访问特定的资源。
基本使用
- 包含头文件
在你的 Qt 项目中,使用 QMutex
之前需要包含相应的头文件:
cpp
#include <QMutex>
- 创建 QMutex 实例
你可以在你的类内部或外部创建一个 QMutex
的实例。通常,这个实例会被声明为类的成员变量,以便在整个类中都能访问它。
cpp
QMutex mutex;
或者作为类的成员:
cpp
class MyClass {
public:
MyClass() {}
void someFunction() {
// 使用 mutex
}
private:
QMutex mutex;
};
- 锁定和解锁
- 锁定(Lock) :在访问共享资源之前,你需要锁定
QMutex
。这可以通过调用lock()
方法实现。如果QMutex
已经被另一个线程锁定,当前线程将被阻塞,直到QMutex
被解锁。
cpp
mutex.lock();
// 访问共享资源
// ...
- 解锁(Unlock) :完成资源访问后,你需要通过调用
unlock()
方法来解锁QMutex
,以便其他线程可以访问该资源。
cpp
// 完成资源访问
mutex.unlock();
- 使用 QMutexLocker(推荐)
虽然直接调用 lock()
和 unlock()
方法可以工作,但 Qt 推荐使用 QMutexLocker
类来自动管理锁的获取和释放。QMutexLocker
是一个在构造时自动锁定 QMutex
,在析构时自动解锁 QMutex
的类。这有助于避免忘记解锁或在发生异常时未解锁的问题。
cpp
QMutex mutex;
void someFunction() {
QMutexLocker locker(&mutex);
// 访问共享资源
// ...
// 当离开此作用域时,locker 对象被销毁,自动调用 unlock()
}
注意事项
- 避免死锁:确保在程序中不会以循环的方式锁定多个
QMutex
,这可能导致死锁。 - 锁粒度:尽量减小锁的范围,只在必要时锁定,以提高程序的并发性能。
- 锁的性能:虽然
QMutex
提供了必要的同步机制,但过多的锁操作可能会降低程序的性能。在设计多线程程序时,要仔细考虑锁的使用。
使用 QMutex
可以帮助你在 Qt 程序中安全地管理多线程对共享资源的访问。
👉🏻QWaitCondition
QWaitCondition是Qt框架中用于线程间同步的一个重要机制,它允许线程等待某个条件的发生,并在条件满足时被唤醒。以下是QWaitCondition的基本使用方法:
- 基本概念
- 条件变量:QWaitCondition本质上是一个条件变量,用于线程间的同步。
- 互斥锁:QWaitCondition通常与QMutex一起使用,以确保在访问共享资源或条件时的线程安全。
- 主要函数
- wait(QMutex* mutex, unsigned long time = ULONG_MAX):使当前线程在指定的互斥锁上等待条件的发生。如果条件未满足,线程将阻塞;如果指定了超时时间(time),则线程将在超时后继续执行。
- wakeOne():唤醒在QWaitCondition上等待的一个线程(如果有的话)。
- wakeAll():唤醒在QWaitCondition上等待的所有线程。
- 使用步骤
等待条件
- 锁定互斥锁:在调用wait()之前,必须首先锁定一个互斥锁,以保护共享资源和条件变量。
- 调用wait():调用QWaitCondition的wait()函数,传入之前锁定的互斥锁。wait()函数内部会释放互斥锁,使其他线程可以修改条件或共享资源。当条件满足时,或超时时间到达时,wait()函数将返回,并重新获取互斥锁。
- 检查条件:从wait()返回后,应检查条件是否真正满足,因为可能存在"虚假唤醒"(spurious wakeup)的情况。
- 解锁互斥锁:在继续执行之前,应解锁互斥锁。
唤醒线程
-
锁定互斥锁:在修改条件或共享资源之前,必须首先锁定互斥锁。
-
修改条件:根据需要进行操作,以改变条件的状态。
-
唤醒线程:使用wakeOne()或wakeAll()函数唤醒等待的线程。
-
解锁互斥锁:在完成操作后,应解锁互斥锁。
-
示例场景
场景一:单个线程等待条件
cpp
QWaitCondition condition;
QMutex mutex;
bool conditionMet = false;
// 等待条件的线程
mutex.lock();
while (!conditionMet) {
condition.wait(&mutex); // 等待条件发生
// 处理条件满足后的操作
}
mutex.unlock();
// 在其他线程中
mutex.lock();
conditionMet = true; // 设置条件满足
condition.wakeOne(); // 唤醒等待的线程
mutex.unlock();
场景二:多个线程等待同一个条件
cpp
QWaitCondition condition;
QMutex mutex;
int counter = 0;
// 等待条件的线程1和线程2
mutex.lock();
while (counter < 10) { // 或其他条件
condition.wait(&mutex); // 等待条件发生
}
mutex.unlock();
// 在其他线程中
mutex.lock();
counter = 15; // 设置条件满足
condition.wakeAll(); // 唤醒所有等待的线程
mutex.unlock();
- 注意事项
- 虚假唤醒:由于系统调度等原因,线程可能会被虚假唤醒。因此,在wait()返回后,应检查条件是否真正满足。
- 超时等待:wait()函数支持超时等待,可以在条件长时间未满足时避免线程永久挂起。
- 线程安全:QWaitCondition和QMutex的所有操作都是线程安全的,可以在多线程环境中安全使用。
通过以上介绍,可以了解到QWaitCondition在Qt多线程编程中的基本使用方法和重要性。
👉🏻QUdpSocket
QUdpSocket是Qt框架中用于UDP网络通信的一个类,它继承自QAbstractSocket,提供了发送和接收UDP数据报的功能。UDP(User Datagram Protocol,用户数据报协议)是一种轻量级、不可靠、面向数据报的无连接协议,适用于对可靠性要求不高的场合。以下是对QUdpSocket的详细介绍:
一、基本概念
- UDP协议:UDP是一种无连接的协议,每个数据报都是独立传输的,不会建立持久的socket连接。因此,UDP通信具有较低的延迟和较高的灵活性,但数据可能会丢失、乱序或重复。
- QUdpSocket:QUdpSocket类用于在Qt应用程序中实现UDP网络通信。通过它,可以发送和接收UDP数据报,实现单播、广播和组播等多种通信模式。
二、主要功能
-
数据报发送
- QUdpSocket提供了多个重载的
writeDatagram
方法来发送数据报。这些方法允许你指定目标地址(IP地址)和端口号,并将数据(通常是QByteArray类型)发送到指定的UDP客户端。 - 数据报的长度一般建议不超过512字节,因为较大的数据报可能会被网络层分片,增加传输的复杂性和错误率。
- QUdpSocket提供了多个重载的
-
数据报接收
- 在接收数据报之前,通常需要使用
bind
方法将QUdpSocket绑定到一个端口上,以便监听传入的数据报。 - 当有数据报到达时,QUdpSocket会发出
readyRead
信号。你可以在相应的槽函数中使用readDatagram
或receiveDatagram
方法来读取数据报。 readDatagram
方法允许你指定一个缓冲区来接收数据报,并可选地获取发送方的地址和端口信息。
- 在接收数据报之前,通常需要使用
-
多播和广播
- QUdpSocket支持UDP多播和广播。通过调用
joinMulticastGroup
方法,可以将QUdpSocket加入到指定的多播组中,以便接收该组内的数据报。 - 要进行广播,只需将目标地址设置为特殊的广播地址(如255.255.255.255),然后发送数据报即可。在同一网络范围内的所有UDP客户端都可以接收到这个广播数据报。
- QUdpSocket支持UDP多播和广播。通过调用
三、使用注意事项
- 端口号选择 :通常选择1024到65535之间的端口号进行UDP通信。1024以下的端口号通常保留给系统或特定应用程序使用。
- 数据报大小:如前所述,建议发送的数据报大小不超过512字节。如果需要发送更大的数据量,可以考虑将数据拆分成多个较小的数据报进行发送。
- 错误处理:在使用QUdpSocket进行UDP通信时,需要注意处理可能发生的错误情况,如网络不可达、端口已被占用等。可以通过检查QUdpSocket的状态或捕获相关的异常来进行错误处理。
四、示例代码
以下是一个简单的QUdpSocket使用示例,展示了如何发送和接收UDP数据报:
cpp
// 发送数据报
QUdpSocket udpSocket;
QByteArray datagram = "Hello, UDP!";
udpSocket.writeDatagram(datagram.data(), datagram.size(), QHostAddress::LocalHost, 12345);
// 接收数据报
udpSocket.bind(QHostAddress::LocalHost, 54321);
connect(&udpSocket, &QUdpSocket::readyRead, this, &MyClass::readPendingDatagrams);
void MyClass::readPendingDatagrams() {
while (udpSocket.hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket.pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
// 处理接收到的数据报...
}
}
在这个示例中,首先创建了一个QUdpSocket对象,并使用writeDatagram
方法发送了一个UDP数据报。然后,使用bind
方法将QUdpSocket绑定到一个端口上,以便接收数据报。最后,通过连接readyRead
信号到槽函数readPendingDatagrams
来读取和处理接收到的数据报。
常用API
-
构造函数和析构函数
QUdpSocket(QObject *parent = nullptr)
: 构造函数,可以指定父对象。~QUdpSocket()
: 析构函数,清理资源。
-
绑定端口
bool bind(const QHostAddress &address = QHostAddress::Any, quint16 port = 0, BindMode mode = DefaultForPlatform)
: 将QUdpSocket绑定到指定的地址和端口上,以便接收数据报。
-
发送数据报
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
: 发送一个数据报到指定的地址和端口。qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
: 发送一个QByteArray类型的数据报到指定的地址和端口。
-
接收数据报
qint64 pendingDatagramSize() const
: 返回下一个待接收的数据报的大小(字节)。qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
: 读取一个数据报到指定的缓冲区,并可选地获取发送方的地址和端口。qint64 receiveDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
: 与readDatagram
类似,但在某些平台上可能更可靠。
-
连接和断开
- 注意:QUdpSocket是无连接的,因此没有像TCP那样的
connect
和disconnect
方法来建立或断开连接。但是,你可以通过bind
来监听端口,并通过writeDatagram
来发送数据报。
- 注意:QUdpSocket是无连接的,因此没有像TCP那样的
-
信号
void readyRead()
: 当有新的数据报到达时发出此信号。void errorOccurred(QAbstractSocket::SocketError socketError)
: 当发生错误时发出此信号,提供错误类型。
-
状态查询
QAbstractSocket::SocketState state() const
: 返回QUdpSocket的当前状态(如未连接、监听中等)。QAbstractSocket::SocketError error() const
: 返回最后发生的错误类型。
-
多播和广播
bool joinMulticastGroup(const QHostAddress &groupAddress)
: 将QUdpSocket加入到指定的多播组中。bool leaveMulticastGroup(const QHostAddress &groupAddress)
: 离开之前加入的多播组。
这些API提供了QUdpSocket进行UDP网络通信所需的基本功能。需要注意的是,由于UDP是无连接的协议,因此在使用QUdpSocket时,不需要像TCP那样建立连接,而是直接发送和接收数据报。
👉🏻QNetworkDatagram
QNetworkDatagram 简介
QNetworkDatagram
是 Qt 网络模块中的一个类,用于处理 UDP 数据报。它可以在无连接的情况下发送和接收数据,非常适合实时通信和小数据量的传输。
基本用法
-
创建 QNetworkDatagram :
你可以通过提供数据和目标地址来创建一个
QNetworkDatagram
对象。cppQByteArray data = "Hello, UDP!"; QHostAddress address = QHostAddress::Broadcast; // 或者指定具体地址 quint16 port = 1234; QNetworkDatagram datagram(data, address, port);
-
发送数据报 :
使用
QUdpSocket
类来发送QNetworkDatagram
。cppQUdpSocket udpSocket; udpSocket.writeDatagram(datagram);
-
接收数据报 :
设置一个
QUdpSocket
来接收数据报,并在可读事件中处理它。cppQUdpSocket udpSocket; udpSocket.bind(QHostAddress::Any, port); // 绑定到指定端口 connect(&udpSocket, &QUdpSocket::readyRead, [&]() { while (udpSocket.hasPendingDatagrams()) { QNetworkDatagram datagram = udpSocket.receiveDatagram(); // 处理接收到的数据 qDebug() << "Received:" << datagram.data() << "from" << datagram.senderAddress() << ":" << datagram.senderPort(); } });
应用场景
- 实时通信:例如,在线游戏、视频会议等应用程序,使用 UDP 以减少延迟。
- 广播和多播:可以通过设置目标地址为广播地址或多播地址,向多个客户端发送数据。
- 传感器数据采集:在物联网应用中,常用于从传感器收集小量数据。
总结
QNetworkDatagram
是一个非常有用的类,可以简化 UDP 数据报的处理。结合 QUdpSocket
,你可以方便地实现高效的网络通信。需要注意的是,UDP 是无连接协议,因此不保证数据的可靠性或顺序。在设计应用时,应根据需求权衡使用。
👉🏻Udp实现客户端和服务端回显
代码链接:Udp实现客户端和服务端回显
实现效果:
👉🏻QTcpServer 和 QTcpSocket
QTcpServer
和 QTcpSocket
是 Qt 网络模块中用于 TCP 网络通信的类。下面是它们的常用 API 和基本用法:
QTcpServer
QTcpServer
用于创建 TCP 服务器,监听客户端的连接请求。
常用 API
-
listen()
:-
启动服务器并开始监听指定的端口。
-
示例:
cppserver->listen(QHostAddress::Any, port);
-
-
nextPendingConnection()
:-
返回下一个挂起的客户端连接。
-
示例:
cppQTcpSocket *clientSocket = server->nextPendingConnection();
-
-
hasPendingConnections()
:- 检查是否有挂起的连接。
-
close()
:- 关闭服务器。
-
信号:
newConnection()
: 当有新的客户端连接时发出。
QTcpSocket
QTcpSocket
用于与 TCP 服务器或其他客户端进行网络通信。
常用 API
-
connectToHost()
:-
连接到指定的主机和端口。
-
示例:
cppsocket->connectToHost("hostname", port);
-
-
disconnectFromHost()
:- 断开与主机的连接。
-
write()
:-
发送数据到远程主机。
-
示例:
cppsocket->write("Hello, Server!");
-
-
readAll()
:- 读取所有可用的数据。
-
state()
:- 获取当前连接状态。
-
信号:
readyRead()
: 当有新数据可读时发出。connected()
: 当成功连接到主机时发出。disconnected()
: 当与主机断开连接时发出。
示例用法
以下是一个简单的 TCP 服务器和客户端示例:
TCP 服务器示例
cpp
QTcpServer *server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, [&]() {
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, &QTcpSocket::readyRead, [&]() {
QByteArray data = clientSocket->readAll();
// 处理接收到的数据
clientSocket->write("Response from server");
});
});
server->listen(QHostAddress::Any, 1234);
TCP 客户端示例
cpp
QTcpSocket *socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::connected, this, [&]() {
socket->write("Hello, Server!");
});
connect(socket, &QTcpSocket::readyRead, this, [&]() {
QByteArray response = socket->readAll();
// 处理服务器响应
});
socket->connectToHost("127.0.0.1", 1234);
TCP回显客户端和服务端
代码链接:
效果:
👉🏻HTTP
进⾏ Qt 开发时, 和服务器之间的通信很多时候也会⽤到 HTTP 协议
QNetworkAccessManager
QNetworkAccessManager
是 Qt 中用于处理网络请求的核心类。以下是一些关键方法:
- get() : 发起一个 GET 请求,返回
QNetworkReply
对象。 - post() : 发起一个 POST 请求,允许发送数据,返回
QNetworkReply
对象。 - put() : 发起一个 PUT 请求,适用于更新资源,返回
QNetworkReply
对象。 - deleteResource(): 发起一个 DELETE 请求,用于删除资源。
- sendCustomRequest(): 发送自定义请求,适用于不常见的 HTTP 方法。
QNetworkRequest
QNetworkRequest
是用于描述网络请求的类。以下是一些核心方法和常用取值:
核心方法
- setUrl(const QUrl &url): 设置请求的 URL。
- setHeader(QNetworkRequest::Header header, const QVariant &value) : 设置请求头,比如
Content-Type
。 - setRawHeader(const QByteArray &headerName, const QByteArray &headerValue): 设置自定义请求头。
- setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value): 设置请求属性,比如 SSL 配置。
- setPriority(QNetworkRequest::Priority priority): 设置请求的优先级。
常用取值
-
Headers:
QNetworkRequest::ContentTypeHeader
: 表示内容类型。QNetworkRequest::ContentLengthHeader
: 表示内容长度。
-
Attributes:
QNetworkRequest::RedirectPolicyAttribute
: 设置重定向策略。QNetworkRequest::HttpPipeliningAllowedAttribute
: 允许 HTTP 管道化。
QNetworkReply
QNetworkReply
是 Qt 中用于处理网络响应的类。以下是一些核心方法:
核心方法
- abort(): 取消当前的请求。
- bytesAvailable(): 返回当前可读取的字节数。
- readAll() : 读取所有响应数据并返回为
QByteArray
。 - error(): 返回请求过程中出现的错误类型。
- attribute(): 获取请求的某个特定属性值。
- rawHeaderList(): 返回所有原始响应头的名称列表。
- rawHeader(const QByteArray &headerName): 获取特定响应头的值。
- url(): 返回请求的 URL。
- finished(): 检查请求是否完成,通常用于信号槽连接。
http发起网络请求
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化对象
manager = new QNetworkAccessManager(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//1.构造QUrl对象
QUrl url(ui->lineEdit->text());
//2.构造request对象
QNetworkRequest request(url);
//3.发起get请求
QNetworkReply* reply = manager->get(request);
//4.通过信号槽来处理响应
connect(reply,&QNetworkReply::finished,this,[=](){
if(reply->error()==QNetworkReply::NoError)
{
//说明请求成功
QString html(reply->readAll());
ui->plainTextEdit->setPlainText(html);
}
else
{
ui->plainTextEdit->setPlainText(reply->errorString());
}
reply->deleteLater();
});
}
👉🏻音视频
QMediaPlayer
和 QVideoWidget
是 Qt Multimedia 模块中的两个重要类,用于处理音视频播放。以下是它们的一些基本方法。
QMediaPlayer
QMediaPlayer
用于播放音频和视频。常用方法包括:
- setMedia(const QMediaContent &media): 设置要播放的媒体内容,可以是文件路径或网络流。
- play(): 开始播放媒体。
- pause(): 暂停播放。
- stop(): 停止播放。
- setVolume(int volume): 设置音量,范围通常是 0 到 100。
- position(): 获取当前播放位置(毫秒)。
- duration(): 获取媒体总时长(毫秒)。
- state(): 获取播放器的当前状态(播放、暂停、停止等)。
- error(): 检查是否有错误发生。
QVideoWidget
QVideoWidget
用于显示视频内容。常用方法包括:
- setMediaPlayer(QMediaPlayer *player) : 将
QMediaPlayer
关联到此视频窗口。 - resizeEvent(QResizeEvent *event): 重载此方法以处理大小调整事件(可选)。
- show(): 显示视频窗口。
- setAspectRatio(Qt::AspectRatioMode aspectRatioMode): 设置视频的显示比例。
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长