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
結語:
無論你是初學者還是有經驗的開發者,我希望我的博客能對你的學習之路有所幫助。如果你覺得這篇文章有用,不妨點擊收藏,或者留下你的評論分享你的見解和經驗,也歡迎你對我博客的內容提出建議和問題。每一次的點贊、評論、分享和關注都是對我的最大支持,也是對我持續分享和創作的動力