Qt網路程式設計概述(一)
Qt網路程式設計概述
QtNetWork模組提供了若干類支援TCP/IP客戶端伺服器端的開發。有如下兩類:
- 低階網路操作:QTcpSocket/QTcpServer/QUdpSocket
- 高階網路操作:QNetworkRequest/QNetworkReply/QNetworkAccessManager
Qt網路程式設計相關類
類名 | 作用 |
---|---|
QAbstractSocket | The base functionality common to all socket types |
QTcpServer | TCP-based server |
QTcpSocket | TCP socket |
QUdpSocket | UDP socket |
QNetworkAccessManager | Allows the application to send network requests and receive replies |
QNetworkReply | Contains the data and headers for a request sent with QNetworkAccessManager |
QNetworkRequest | Holds a request to be sent with QNetworkAccessManager |
QNetworkCookie | Holds one network cookie |
QNetworkCookieJar | Implements a simple jar of QNetworkCookie objects |
QNetworkSession | Control over the system’s access points and enables session management for cases when multiple clients access the same access point |
QLocalSocket | Local socket |
QLocalServer | Local socket based server |
QHttpPart | Holds a body part to be used inside a HTTP multipart MIME message |
QHttpMultiPart | Resembles a MIME multipart message to be sent over HTTP |
QHostInfo | Static functions for host name lookups |
QHostAddress | IP address |
高階網路操作-HTTP/FTP
Qt網路程式設計提供了大量的API用於網路操作。API為特定的操作和協議提供了一個抽象層(如通過HTTP收發資料)。主要有如下幾大核心類:
-
QNetworkRequest網路請求:充當請求關聯資訊的容器,如請求頭、加密等。當請求物件構造時指定了URL,也就確定了請求協議。目前支援使用HTTP/FTP/本地檔案URL上傳下載檔案。
-
QNetworkReply請求響應:當用戶請求發出後,該物件由QNetworkAccessManager建立。QNetworkReply發出的訊號可獨立的監控每個請求響應,或者用QNetworkAccessManager的訊號替代。QNetworkReply是QIODevice的子類,因此響應可以被同步或非同步的處理(阻塞或非阻塞)。
-
QNetworkAccessManager操作管理:請求建立後,管理類會分發請求,然後對外發送訊號標識請求進度。還可使用cookies在客戶端儲存資料、請求認證、代理使用。
TCP程式設計QTcpSocket/QTcpServer
TCP(傳輸控制協議)是被大多網路協議使用的低級別協議,如HTTP/FTP。它是可靠的、面向流的、面向連線的傳輸協議。特別適合於連續的資料傳輸。QTcpSocket類為TCP提供了若干介面。可用QTcpSocket實現標準網路協議,如POP3、SMTP、NNTP、自定義協議。
在開始資料轉移前,需要與遠端主機和埠建立TCP連線。一旦建立連線,對等方的IP地址和埠可通過QTcpSocket::peerAddress()和QTcpSocket::peerPort()獲取。在任意時刻,對等方可以關閉連線,此時資料轉移會立即終止。
QTcpSocket以非同步的方式工作,並通過傳送訊號報告狀態的改變及錯誤,類似於QNetworkAccessManger。通過事件迴圈檢測輸入資料,並自動的Flush輸出資料。可使用QTcpSocket::write()往socket中寫入資料,QTcpSocket::read()向socket中寫入資料。QTcpSocket有兩個獨立的資料流:讀和寫。由於QTcpSocket繼承自QIODevice,可以和QTextStream、QDataStream一塊使用。當從QTcpSocket讀取資料時,可呼叫QTcpSocket::bytesAvailable()確保有足夠的資料。
QTcpServer類處理接收到的TCP連線。一般使用邏輯如下:
- 呼叫QTcpServer::listen()啟動服務端並監聽;
- 每當接收到客戶端的請求,就會發送QTcpServer::newConnection()訊號;
- 在槽函式中,呼叫QTcpServer::nextPendingConnection()接受請求,並用返回的QTcpSocket與客戶端通訊。
QTcpSocket類客戶端建立TCP連線。一般使用邏輯如下:
- 呼叫QTcpSocket::connectToHost連線到主機;
- 連線readyRead()訊號槽;
- 在槽函式中與服務端互動。
儘管QTcpSocket大部分函式是以非同步的方式工作,但仍可用同步的方式處理(阻塞)。為了以阻塞方式,需呼叫QTcpSocket的waitFor…()函式,它會掛起呼叫執行緒直到結束訊號被髮送。例如,呼叫非阻塞函式QTcpSocket::connectToHost()時,呼叫QTcpSocket::waitForConnected()阻塞執行緒直到connected()訊號被髮射。QTcpSocket的waitFor…()的弊端也很明顯:當阻塞時,事件不會被處理,如果在GUI執行緒中,會阻塞應用程式的介面。因此,推薦在非GUI執行緒中使用同步sockets(同步處理時,QTcpSocket無需事件迴圈)。
UDP程式設計QUdpSocket
UDP(使用者資料報協議)是一個輕量級、不可靠的、面向資料報的、無連線的協議。當可靠性不是很重要、速度很重要時,可使用該協議。例如,伺服器端選擇UDP報告每天的時間,如果期間的某個資料報丟失,客戶端可另外請求資料。
QUdpSocket類允許收發資料報。與TCP的主要區別是:UDP傳輸的是資料包而不是連續的資料流。資料包是大小有限的資料單元(通常小於512位元組),除了包含的傳輸資料外,還包含收發物件的IP地址和埠號。一般使用邏輯如下:
- QUdpSocket::bind()接受傳入的資料包;
- 接收到資料包後,QUdpSocket傳送readyRead()訊號;
- 呼叫QUdpSocket::readDatagram()讀取資料。
解析主機名QHostName
在建立網路連線前,QTcpSocket和QUdpSocket執行域名查詢,將連線的主機名轉化為IP地址(DNS)。提供如下兩種方式:
- QHostInfo::lookupHost() 非同步查詢,通過訊號槽機制,查詢在單獨的執行緒中實現。
- QHostInfo::forName() 同步查詢,在呼叫者所線上程實行查詢。注意在GUI執行緒中呼叫,可能會阻塞介面。
程式碼示例如下:
標頭檔案
//////////////////////////////////////////////////////////////////////////
//主機資訊測試驗證
#include <QObject>
#include <QHostInfo>
class HostInfoDemo : public QObject
{
Q_OBJECT
private slots:
void lookUp(const QHostInfo& oHostInfo);
};
void testHostInfoDemo();
原始檔
#include "HostInfoDemo.h"
#include <QDebug>
void HostInfoDemo::lookUp(const QHostInfo& oHostInfo)
{
if (oHostInfo.error() != QHostInfo::NoError)
{
qDebug() << "failed: " << oHostInfo.errorString();
return;
}
const auto addresses = oHostInfo.addresses();
for (const QHostAddress &oAddress : addresses)
{
qDebug() << "asynchronous Found address: " << oAddress.toString();
}
}
void testHostInfoDemo()
{
//非同步方式
qDebug() << QStringLiteral("非同步方式...");
HostInfoDemo* pDemo = new HostInfoDemo;
QHostInfo::lookupHost("www.kde.org", pDemo, SLOT(lookUp(QHostInfo)));
//同步方式
qDebug() << QStringLiteral("同步方式...");
QHostInfo oHostInfo = QHostInfo::fromName("www.kde.org");
const auto addresses = oHostInfo.addresses();
for (const QHostAddress &oAddress : addresses)
{
qDebug() << "synchronous Found address: " << oAddress.toString();
}
}
輸出資訊
"非同步方式..."
"同步方式..."
synchronous Found address: "91.189.93.5"
asynchronous Found address: "91.189.93.5"