Skip to content

网络与多线程

网络编程

UDP编程

主要的类有两个:

  • Qudpsocket
  • QNetworkDatagram

Qudpsocket

Qudpsocket 表示一个 UDP 的 socket

名称类型说明对标原生 API
bind(const QHostAddress&, quint16)方法绑定指定的端口号bind
receiveDatagram()方法返回 QNetworkDatagram,读取一个 UDP 数据报recvfrom
writeDatagram(const QNetworkDatagram&)方法发送一个 UDP 数据报sendto
readyRead信号在收到数据并准备就绪后触发无 (类似于 IO 多路复用的通知机制)

QNetworkDatagram

QNetworkDatagram 表示一个 UDP 数据报

名称类型说明对标原生 API
QNetworkDatagram(const QByteArray&, const QHostAddress&, quint16)构造函数通过 QByteArray、目标 IP 地址、目标端口号构造一个 UDP 数据报(通常用于发送数据)
data()方法获取数据报内部特有的数据(返回 QByteArray)
senderAddress()方法获取数据报中包含的对端 IP 地址无(recvfrom 包含该功能)
senderPort()方法获取数据报中包含的对端端口号无(recvfrom 包含该功能)

TCP编程

核心类有两个:

  • QTcpServer
  • QTcpSocket

QTcpServer

QTcpServer 用于监听端口和获取客户端连接

名称类型说明对标原生 API
listen(const QHostAddress&, quint16 port)方法绑定指定的地址和端口号,并开始监听bind 和 listen
nextPendingConnection()方法获取已建立的 TCP 连接(返回 QTcpSocket 对象,用于与客户端通信)accept
newConnection信号当新的客户端建立连接后触发无(类似于 IO 多路复用中的通知机制)

QTcpSocket

QTcpSocket 用于客户端和服务器之间的数据交互

名称类型说明对标原生 API
readAll()方法读取当前接收缓冲区中的所有数据read
write(const QByteArray&)方法将数据写入 socketwrite
deleteLater方法标记 socket 对象为无效(Qt 在下个事件循环中释放该对象)无(类似于半自动化的垃圾回收)
readyRead信号当数据到达并准备就绪时触发无(类似于 IO 多路复用中的通知机制)
disconnected信号当连接断开时触发无(类似于 IO 多路复用中的通知机制)

HTTP Client

进行 Qt 开发时,与服务器的通信常使用 HTTP 协议:

  • 通过 HTTP 从服务器获取数据
  • 通过 HTTP 向服务器提交数据

核心类是三个:

  • QNetworkAccessManager
  • QNetworkRequest
  • QNetworkReply

QNetworkAccessManager

方法说明
get(const QNetworkRequest&)发起 HTTP GET 请求(返回 QNetworkReply 对象)
post(const QNetworkRequest&, const QByteArray&)发起 HTTP POST 请求(返回 QNetworkReply 对象)

QNetworkRequest

方法说明
QNetworkRequest(const QUrl&)通过 URL 构造 HTTP 请求
setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)设置请求头

请求头取值

取值说明
ContentTypeHeader描述 body 的类型
ContentLengthHeader描述 body 的长度
LocationHeader用于重定向报文中指定重定向地址(响应中使用)
CookieHeader设置 cookie
UserAgentHeader设置 User-Agent

QNetworkReply

QNetworkReply 表示一个 HTTP 响应(继承自 qroDevice

方法说明
error()获取出错状态
errorString()获取出错原因的文本描述
readAll()读取响应 body
header(QNetworkRequest::KnownHeaders header)读取指定响应头的值

重要信号

  • finished():当客户端收到完整的响应数据后触发

多线程编程

QThread

QThread 是 Qt 框架中处理多线程编程的类(父类为 QObject),继承了信号槽机制、对象树管理等特性。

QThread 的特点

  1. 线程创建和管理

    • 简单的线程创建:继承 QThread 并重写 run() 函数(线程入口点)
    • 生命周期管理
      • start():启动线程
      • quit():优雅地请求线程退出
      • terminate():强制终止线程(可能导致资源泄漏,尽量避免使用)
  2. 信号和槽机制在线程中的应用

    • 线程间通信:通过信号槽实现跨线程通信(例如任务完成时通知主线程)
    • 异步操作:在后台线程执行耗时任务,通过信号返回结果
  3. 资源管理

    • 对象树机制:QThread 参与 Qt 对象树管理(父对象销毁时自动清理)
    • 线程局部存储:通过 threadLocalStorage() 安全访问线程局部变量
  4. 可重入性和线程安全性

    • 可重入函数:QThread 的非静态成员函数可被多线程同时调用(不访问共享数据时)
    • 线程安全的操作:内部同步处理确保启动/停止线程的正确性

常用 API

方法说明
run()线程入口函数(需重写)
start()调用 run() 启动线程(若线程已在运行则无效)
currentThread()返回管理当前线程的 QThread 指针
isRunning()线程运行时返回 true
sleep()/msleep()/usleep()使线程休眠(秒/毫秒/微秒)
wait()阻塞线程直至:1. 线程执行完成 2. 超时(默认 ULONG_MAX 永不超时)
terminate()强制终止线程(需谨慎使用)
finished()信号:线程结束时触发(用于清理工作)

使用步骤

  1. 自定义类继承 QThread
  2. 重写 run() 函数(包含需执行的复杂逻辑)
  3. 启动线程:调用 start()(非直接调用 run()
  4. 通过信号通知主线程任务完成
  5. 关闭线程

多线程使用事项

  • 禁止在线程函数中操作 UI
    • 原因1(线程安全性):UI 操作应由主线程管理(GUI 框架非线程安全)
    • 原因2(事件循环机制):跨线程操作 UI 会干扰主线程事件循环(导致界面异常)
  • connect() 的第五参数 Qt::ConnectionType
    连接类型说明
    Qt::AutoConnection自动选择(同线程用 Direct,跨线程用 Queued)
    Qt::DirectConnection信号发出后槽函数立即在同一线程执行(需确保线程安全)
    Qt::QueuedConnection槽函数插入接收者线程事件队列(跨线程安全)
    Qt::BlockingQueuedConnection发送线程阻塞直至槽函数执行完毕(注意死锁风险)
    Qt::UniqueConnection标志位(可与其他类型组合,确保唯一连接)

线程安全

常用同步类:

  • 互斥锁:QMutex、QMutexLocker
  • 条件变量:QWaitCondition
  • 信号量:QSemaphore
  • 读写锁:QReadLocker、QWriteLocker、QReadWriteLock

互斥锁

QMutex

cpp
QMutex mutex;
mutex.lock();   // 上锁
// 访问共享资源
mutex.unlock(); // 解锁

QMutexLocker(RAII 方式管理锁)

cpp
QMutex mutex;
{
    QMutexLocker locker(&mutex); // 自动上锁
    // 访问共享资源
} // 作用域结束自动解锁

读写锁

cpp
QReadWriteLock rwLock;
// 读操作
{
    QReadLocker locker(&rwLock); // 上读锁(允许多线程读)
    // 读取共享资源
}
// 写操作
{
    QWriteLocker locker(&rwLock); // 上写锁(独占访问)
    // 修改共享资源
}

条件变量

QWaitCondition

cpp
QMutex mutex;
QWaitCondition condition;
// 等待线程
mutex.lock();
while (!conditionFullfilled()) {
    condition.wait(&mutex); // 释放锁并等待
}
// 条件满足后继续执行
mutex.unlock();

// 通知线程
mutex.lock();
changeCondition();
condition.wakeAll(); // 唤醒所有等待线程
mutex.unlock();

信号量

QSemaphore

cpp
QSemaphore semaphore(2); // 允许2个线程并发访问
semaphore.acquire();     // 获取信号量(资源不足时阻塞)
// 访问共享资源
semaphore.release();     // 释放信号量

作业

完成聊天软件的网络部分

知识如风,常伴吾身