Qt Network 模块
Qt Network 模块提供了丰富的类用于实现各种网络通信功能,涵盖 TCP、UDP、HTTP、FTP 等多种协议。
Qt 网络类均为异步操作,通过信号槽处理结果,避免阻塞 UI 线程。
在使用QT进行网络编程之前,就必须在 CMakeLists.txt 中显式启用并链接 Qt6::Network
CMake:
find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(mytarget PRIVATE Qt6::Network)
Qt Network 模块最常用的核心类及其主要用途:
基础网络类
QHostAddress
- 表示 IP 地址(支持 IPv4 和 IPv6),提供地址解析、转换和比较功能
- 常用场景:指定服务器地址、客户端连接目标地址等
QNetworkInterface
- 提供本地网络接口(如网卡)的信息,包括 IP 地址、子网掩码、MAC 地址等
- 常用方法:
allInterfaces()
获取所有网络接口,addressEntries()
获取接口的 IP 地址列表
TCP 通信类
QTcpSocket
- TCP 客户端套接字,用于与 TCP 服务器建立连接并进行可靠的数据传输
- 核心信号:
connected()
(连接成功)、readyRead()
(收到数据)、disconnected()
(连接断开) - 核心方法:
connectToHost()
(连接服务器)、write()
(发送数据)、readAll()
(读取数据)
QTcpServer
- TCP 服务器类,用于监听端口并接受客户端连接
- 核心信号:
newConnection()
(有新客户端连接) - 核心方法:
listen()
(开始监听)、nextPendingConnection()
(获取新连接的套接字)
UDP 通信类
QUdpSocket
- 用于 UDP 协议的无连接数据报通信,适用于实时性要求高的场景(如语音、视频)
- 核心信号:
readyRead()
(收到数据报) - 核心方法:
bind()
(绑定端口)、writeDatagram()
(发送数据报)、readDatagram()
(接收数据报)
HTTP / 网络请求类
QNetworkAccessManager
- 管理网络请求的核心类,支持 HTTP、HTTPS、FTP 等协议,可发送 GET、POST 等请求
- 核心方法:
get()
(发送 GET 请求)、post()
(发送 POST 请求)、head()
(发送 HEAD 请求) - 核心信号:
finished(QNetworkReply*)
(请求完成)
QNetworkRequest
- 封装网络请求的信息,如 URL、请求头(Header)、超时设置等
- 常用方法:
setUrl()
(设置请求 URL)、setHeader()
(设置请求头,如 Content-Type)
QNetworkReply
- 表示网络请求的响应,包含服务器返回的数据、状态码、错误信息等
- 核心信号:
readyRead()
(有数据可读取)、downloadProgress()
(下载进度) - 核心方法:
readAll()
(读取响应数据)、error()
(获取错误信息)、attribute()
(获取响应属性,如状态码)
域名解析类
QHostInfo
- 用于域名解析(将域名转换为 IP 地址),支持异步和同步解析
- 常用方法:
lookupHost()
(异步解析域名)、fromName()
(同步解析域名) - 解析结果通过
QHostInfo
对象返回,包含解析到的QHostAddress
列表
网络配置类
QNetworkConfiguration
- 表示网络配置(如 Wi-Fi、有线网络),包含网络的状态和类型信息
QNetworkConfigurationManager
- 管理系统中的网络配置,可获取可用网络、监听网络状态变化
- 核心信号:
configurationChanged()
(网络配置变化)、onlineStateChanged()
(在线状态变化)
SSL/TLS 安全通信类
QSslSocket
- 继承自
QTcpSocket
,支持 SSL/TLS 加密通信,用于 HTTPS、FTPS 等安全协议 - 核心方法:
startClientEncryption()
(启动客户端加密)、setCaCertificates()
(设置 CA 证书)
- 继承自
QHostAddress类
This class holds an IPv4 or IPv6 address in a platform- and protocol-independent manner.
QHostAddress is normally used with the QTcpSocket, QTcpServer, and QUdpSocket to connect to a host or to set up a server.
A host address is set with setAddress(), and retrieved with toIPv4Address(), toIPv6Address(), or toString(). You can check the type with protocol().
Note: Please note that QHostAddress does not do DNS lookups. QHostInfo is needed for that.
The class also supports common predefined addresses: Null, LocalHost, LocalHostIPv6, Broadcast, and Any.
该类以独立于平台和协议的方式保存IPv4或IPv6地址。
QHostAddress通常与QTcpSocket、QTcpServer和QUdpSocket一起使用,用于连接主机或设置服务器。
主机地址用setAddress()设置,用toIPv4Address()、toIPv6Address()或toString()检索。您可以使用protocol()检查类型。
注意:请注意,QHostAddress不做DNS查找。为此需要QHostInfo。
该类还支持常见的预定义地址:Null、LocalHost、LocalHostIPv6、Broadcast和Any。
TCP 通信类
QTcpSocket类
TCP (Transmission Control Protocol) is a reliable, stream-oriented, connection-oriented transport protocol. It is especially well suited for continuous transmission of data
QTcpSocket is a convenience subclass of QAbstractSocket that allows you to establish a TCP connection and transfer streams of data
TCP(传输控制协议)是一种可靠的、面向流的、面向连接的传输协议。它特别适合于数据的连续传输
QTcpSocket是QAbstractSocket的一个便捷的子类,它允许您建立TCP连接并传输数据流
连接状态标志 :
peerAddress() 返回服务器的 IP 地址(QHostAddress 类型)
peerPort() 返回服务器的端口号(quint16 类型)
localAddress() 返回本地绑定的 IP 地址(QHostAddress 类型)
localPort() 返回本地绑定的端口号(quint16 类型)
connectToHost(const QString &host, quint16 port)连接到指定主机(支持域名或 IP)和端口(异步操作)
connectToHost(const QHostAddress &address, quint16 port)连接到指定 QHostAddress 表示的 IP 和端口
disconnectFromHost()主动断开连接(发送 FIN 包,优雅关闭,触发 disconnected 信号)
abort() 强制终止连接(立即关闭,不发送 FIN 包,适合超时或错误处理)
connected() 成功连接到服务器时触发
disconnected() 与服务器断开连接时触发
readyRead() 有数据可读取时触发(最常用,需配合 readAll() 等方法读取)
bytesWritten(qint64 bytes) 数据写入底层缓冲区后触发(bytes 为实际写入的字节数)
errorOccurred(QAbstractSocket::SocketError error) 发生错误时触发(error 为错误类型枚举)
stateChanged(QAbstractSocket::SocketState state) 连接状态变化时触发(如从连接中变为已连接QAbstractSocket::SocketError 枚举
常见错误类型:
- ConnectionRefusedError:连接被拒绝
- HostNotFoundError:主机未找到
- TimeoutError:超时
- RemoteHostClosedError:服务器主动关闭
QTcpServer类
QTcpServer类使得接受传入的TCP连接成为可能。您可以指定端口,也可以让QTcpServer自动选择一个。你可以监听一个特定的地址,也可以监听所有机器的地址。
调用listen()让服务器侦听传入的连接。然后,每次客户端连接到服务器时都会发出newConnection()信号。当使用addPendingConnection()函数将客户端连接添加到挂起连接队列中时,将发出pendingConnectionAvailable()信号。
调用nextPendingConnection()将挂起的连接作为已连接的QTcpSocket接受。该函数返回QAbstractSocket::ConnectedState中指向QTcpSocket的指针,您可以使用该指针与客户端进行通信。
如果发生错误,serverError()返回错误的类型,并且可以调用errorString()来获得关于发生的事情的人类可读的描述。
当监听连接时,服务器正在监听的地址和端口可以作为serverAddress()和serverPort()使用。
调用close()使QTcpServer停止侦听传入的连接。
虽然QTcpServer主要是为与事件循环一起使用而设计的,但是不使用事件循环也可以使用它。在这种情况下,必须使用waitForNewConnection(),它会阻塞,直到连接可用或超时过期。
isListening() 返回服务器是否正在监听端口(bool 类型)
serverAddress() 返回服务器监听的 IP 地址(QHostAddress 类型,如 QHostAddress::Any)
serverPort() 返回服务器监听的端口号(quint16 类型,未监听时返回 0)
maxPendingConnections() 返回最大等待处理的连接数(默认 30),超过则拒绝新连接
setMaximumPendingConnections(int num) 设置最大等待处理的连接数
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
开始监听指定地址和端口
- address:监听的 IP 地址(Any 表示所有网络接口)
- port:端口号(0 表示随机分配)
close() 停止监听并关闭所有已建立的连接(触发客户端 disconnected 信号)
extPendingConnection() 获取下一个等待处理的客户端连接(返回 QTcpSocket*)
需手动管理该对象生命周期(建议设置父对象)
hasPendingConnections() 判断是否有等待处理的客户端连接(bool 类型)
pendingConnections() 返回所有等待处理的连接列表(QList<QTcpSocket*>)
newConnection() 有新客户端连接请求且已建立连接时触发(需调用 nextPendingConnection() 获取连接)
acceptError(QAbstractSocket::SocketError error) 接受连接发生错误时触发(如超过最大连接数)
serverError(QAbstractSocket::SocketError error) 服务器发生错误时触发(如监听失败)
QAbstractSocket::SocketError 枚举
错误类型:
- AddressInUseError:地址已被占用
- PermissionDeniedError:权限不足
- InvalidAddressError:无效地址
成果展示:
模拟网络调试助手,实现TCP客户端与服务端交互
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void connectTCPService();private slots://接收数据void receiveServerMessage();//连接按键void on_connect_clicked();//断开连接按键void on_disconnect_clicked();//发送按键void on_send_clicked();//清除发送框文本void on_Sclear_clicked();//清楚接收框文本void on_Rclear_clicked();private:Ui::MainWindow *ui;QTcpSocket *tcpsocket;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QString>
#include <QDebug>
#include <QDateTime>
#include <QNetworkProxy>
#include <QPlainTextEdit>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//创建TCPsocket对象tcpsocket = new QTcpSocket(this);//当发出readyDead信号connect(tcpsocket,&QTcpSocket::readyRead,this,&MainWindow::receiveServerMessage);connect(tcpsocket, &QTcpSocket::connected, this, [](){qDebug() << "连接服务器成功!";});connect(tcpsocket, &QTcpSocket::errorOccurred, this, [this](QAbstractSocket::SocketError err){qDebug() << "连接失败:" << tcpsocket->errorString();});
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::connectTCPService()
{//获取端口号和IP地址QString ip = ui->ip->toPlainText();QString port = ui->port->toPlainText();if(ip.isEmpty() || port.isEmpty()){qDebug() << "ip or port is empty!";return;}//进行链接QHostAddress hostAddress(ip);if(tcpsocket->state() == QAbstractSocket::UnconnectedState){// 关键:禁用代理,避免代理类型错误tcpsocket->setProxy(QNetworkProxy::NoProxy);tcpsocket->connectToHost(hostAddress,(quint16)port.toUInt());}
}
void MainWindow::receiveServerMessage()
{//获取系统当前时间QString currenttime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz");//获取IPQString TcpIPaddress = tcpsocket->peerAddress().toString();//获取端口号quint16 TcpPort = tcpsocket->peerPort();//读取数据QByteArray readdata = tcpsocket->readAll();//与服务器约定并统一使用 UTF-8 编码,可彻底解决中文接收乱码问题QString result = QString("[%1]# SEND ASCII/82 to ALL CLIENTS >>>\n%4").arg(currenttime).arg(QString::fromUtf8(readdata));QString PlainText = ui->receive_text->toPlainText();if(!PlainText.isEmpty()){PlainText.append("\n");}PlainText.append(result);ui->receive_text->setPlainText(PlainText);
}
void MainWindow::on_connect_clicked()
{qDebug() << "connectclicked";connectTCPService();
}void MainWindow::on_disconnect_clicked()
{qDebug() << "disconnectclicked";//断开连接if(tcpsocket->state() == QAbstractSocket::ConnectedState){tcpsocket->disconnectFromHost();}
}void MainWindow::on_send_clicked()
{QString currenttime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz");// 获取本地IP和端口(客户端自身)QString localIP = tcpsocket->localAddress().toString();quint16 localPort = tcpsocket->localPort();// 读取发送框内容QString sendText = ui->send_text->toPlainText().trimmed();if (sendText.isEmpty()){//空内容不发送,可添加提示return;}// 按UTF-8编码转换为字节流发送QByteArray sendData = sendText.toUtf8();qint64 bytesSent = tcpsocket->write(sendData);/*//发送成功后更新界面显示if (bytesSent != -1){QString result = QString("[%1]# RECV ASCII/82 from %2 :%3 <<<%4").arg(currenttime).arg(localIP).arg(localPort).arg(sendText);QString PlainText = ui->receive_text->toPlainText();if (!PlainText.isEmpty()){PlainText.append("\n");}PlainText.append(result);ui->receive_text->setPlainText(PlainText);}else{//发送失败提示QString errorMsg = QString("[%1]# \n发送失败: %2").arg(currenttime).arg(tcpsocket->errorString());ui->receive_text->setPlainText(errorMsg);}*/
}
void MainWindow::on_Sclear_clicked()
{// 清空发送框ui->send_text->clear();
}void MainWindow::on_Rclear_clicked()
{// 清空发送框ui->receive_text->clear();
}
实现 TCP客户端与服务端交互,对文本框的清除效果
博主已经将项目文件压缩上传,若有感兴趣的同学,可以下载使用
UDP 通信类
QUdpSocket类
UDP (User Datagram Protocol) is a lightweight, unreliable, datagram-oriented, connectionless protocol. It can be used when reliability isn't important. QUdpSocket is a subclass of QAbstractSocket that allows you to send and receive UDP datagrams.
The most common way to use this class is to bind to an address and port using bind(), then call writeDatagram() and readDatagram() / receiveDatagram() to transfer data. If you want to use the standard QIODevice functions read(), readLine(), write(), etc., you must first connect the socket directly to a peer by calling connectToHost().
The socket emits the bytesWritten() signal every time a datagram is written to the network. If you just want to send datagrams, you don't need to call bind().
The readyRead() signal is emitted whenever datagrams arrive. In that case, hasPendingDatagrams() returns true. Call pendingDatagramSize() to obtain the size of the first pending datagram, and readDatagram() or receiveDatagram() to read it.
UDP(用户数据报协议)是一种轻量级、不可靠、面向数据报、无连接的协议。它可以在可靠性不重要的情况下使用。QUdpSocket是QAbstractSocket的一个子类,它允许您发送和接收UDP数据报。
使用QUdpSocket类最常见的方法是使用bind()绑定到一个地址和端口,然后调用writeDatagram()和readDatagram() / receiveDatagram()来传输数据。如果你想使用标准的QIODevice函数read(), readLine(), write()等,你必须首先通过调用connectToHost()将套接字直接连接到对等体。
套接字每次将数据报写入网络时都会发出bytesWritten()信号。如果您只想发送数据报,则不需要调用bind()。
每当数据报到达时,就会发出readyRead()信号。在这种情况下,hasPendingDatagrams()返回true。调用pendingDatagramSize()来获取第一个挂起数据报的大小,并调用readDatagram()或receiveDatagram()来读取它。
绑定端口
bool bind(const QHostAddress &address, quint16 port);
用于将套接字绑定到特定的地址和端口,以便接收数据。发送数据
qint64 writeDatagram(const QByteArray &datagram,
const QHostAddress &host, quint16 port);
向指定的主机和端口发送 UDP 数据报。接收数据
qint64 readDatagram(char *data, qint64 maxSize,
QHostAddress *host = nullptr, quint16 *port = nullptr);
从套接字中读取接收到的数据报,并获取发送方的地址和端口QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize = -1)
从套接字接收一个 UDP 数据报,并将其封装为 QNetworkDatagram 对象返回
QNetworkDatagram 对象,包含数据内容、发送方地址、端口等信息信号
readyRead(): 当有数据报到达时触发
errorOccurred(QAbstractSocket::SocketError): 发生错误时触发
成果展示:
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QNetworkProxy>
#include <QDebug>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//创建TCPsocket对象udpsocket = new QUdpSocket(this);//当有数据可读时触发connect(udpsocket, &QUdpSocket::readyRead, this, &MainWindow::receiveMessage);//错误处理connect(udpsocket, &QUdpSocket::errorOccurred, this, [this](QAbstractSocket::SocketError err){QString errorMsg = QString("错误: %1").arg(udpsocket->errorString());qDebug() << errorMsg;});
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::connectUDP()
{//获取端口号和IP地址QString ip = ui->IP->text();QString port = ui->Port->text();if(ip.isEmpty()){qDebug() << "ip or port is empty!";return;}//进行链接QHostAddress hostAddress(ip);if(udpsocket->state() == QAbstractSocket::UnconnectedState){// 关键:禁用代理,避免代理类型错误udpsocket->setProxy(QNetworkProxy::NoProxy);bool result = udpsocket->bind(hostAddress, (quint16)port.toUInt());if(result){qDebug() <<"绑定成功";}else{QString errorMsg = QString("绑定失败: %1").arg(udpsocket->errorString());qDebug() << errorMsg;}}
}void MainWindow::receiveMessage()
{// 循环处理所有待接收的数据报while (udpsocket->hasPendingDatagrams()){//获取系统当前时间QString currenttime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz");// 获取待接收数据报的大小qint64 datagramSize = udpsocket->pendingDatagramSize();QByteArray rawData(datagramSize, 0); // 用 QByteArray 替代 char*,自动管理内存QHostAddress senderAddress;quint16 senderPort;//读取数据报qint64 bytesRead = udpsocket->readDatagram(rawData.data(), datagramSize, &senderAddress, &senderPort);this->senderAddress = senderAddress;this->senderPort = senderPort;//与服务器约定并统一使用 UTF-8 编码,可彻底解决中文接收乱码问题QString result = QString("[%1]# SEND ASCII/12 to %2 :%3 >>>\n%4").arg(currenttime).arg(senderAddress.toString()).arg(senderPort).arg(QString::fromUtf8(rawData));QString PlainText = ui->receive_txt->toPlainText();if(!PlainText.isEmpty()){PlainText.append("\n");}PlainText.append(result);ui->receive_txt->setPlainText(PlainText);}
}void MainWindow::on_connect_clicked()
{qDebug() << "connectclicked";connectUDP();
}void MainWindow::on_disconnect_clicked()
{qDebug() << "disconnectclicked";udpsocket->close();ui->receive_txt->setPlainText(QString("已解除绑定,停止监听\n"));
}void MainWindow::on_send_clicked()
{QString currenttime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz");// 获取本地IP和端口(客户端自身)QString localIP = udpsocket->localAddress().toString();quint16 localPort = udpsocket->localPort();// 读取发送框内容QString sendText = ui->send_txt->toPlainText().trimmed();if (sendText.isEmpty()){//空内容不发送,可添加提示return;}// 按UTF-8编码转换为字节流发送QByteArray sendData = sendText.toUtf8();qint64 result =udpsocket->writeDatagram(sendData, this->senderAddress, this->senderPort);if(result == -1){//发送失败qDebug() << "发送失败:" << udpsocket->errorString();}else{//发送成功qDebug() << "成功发送" << result << "字节到" << this->senderAddress << ":" << this->senderPort;}
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void connectUDP();//接收数据void receiveMessage();//目标IP----可更改为数组,容器等形式,实现向多个端口发送QHostAddress senderAddress;//目标端口quint16 senderPort;
private slots:void on_connect_clicked();void on_disconnect_clicked();void on_send_clicked();private:Ui::MainWindow *ui;QUdpSocket *udpsocket;};
#endif // MAINWINDOW_H
结语:
无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力