1. 程式人生 > >Qt網路程式設計概述(一)

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、自定義協議。

image

在開始資料轉移前,需要與遠端主機和埠建立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連線。一般使用邏輯如下:

  1. 呼叫QTcpServer::listen()啟動服務端並監聽;
  2. 每當接收到客戶端的請求,就會發送QTcpServer::newConnection()訊號;
  3. 在槽函式中,呼叫QTcpServer::nextPendingConnection()接受請求,並用返回的QTcpSocket與客戶端通訊。

QTcpSocket類客戶端建立TCP連線。一般使用邏輯如下:

  1. 呼叫QTcpSocket::connectToHost連線到主機;
  2. 連線readyRead()訊號槽;
  3. 在槽函式中與服務端互動。

儘管QTcpSocket大部分函式是以非同步的方式工作,但仍可用同步的方式處理(阻塞)。為了以阻塞方式,需呼叫QTcpSocket的waitFor…()函式,它會掛起呼叫執行緒直到結束訊號被髮送。例如,呼叫非阻塞函式QTcpSocket::connectToHost()時,呼叫QTcpSocket::waitForConnected()阻塞執行緒直到connected()訊號被髮射。QTcpSocket的waitFor…()的弊端也很明顯:當阻塞時,事件不會被處理,如果在GUI執行緒中,會阻塞應用程式的介面。因此,推薦在非GUI執行緒中使用同步sockets(同步處理時,QTcpSocket無需事件迴圈)。

UDP程式設計QUdpSocket

UDP(使用者資料報協議)是一個輕量級、不可靠的、面向資料報的、無連線的協議。當可靠性不是很重要、速度很重要時,可使用該協議。例如,伺服器端選擇UDP報告每天的時間,如果期間的某個資料報丟失,客戶端可另外請求資料。

image

QUdpSocket類允許收發資料報。與TCP的主要區別是:UDP傳輸的是資料包而不是連續的資料流。資料包是大小有限的資料單元(通常小於512位元組),除了包含的傳輸資料外,還包含收發物件的IP地址和埠號。一般使用邏輯如下:

  1. QUdpSocket::bind()接受傳入的資料包;
  2. 接收到資料包後,QUdpSocket傳送readyRead()訊號;
  3. 呼叫QUdpSocket::readDatagram()讀取資料。

解析主機名QHostName

在建立網路連線前,QTcpSocket和QUdpSocket執行域名查詢,將連線的主機名轉化為IP地址(DNS)。提供如下兩種方式:

  1. QHostInfo::lookupHost() 非同步查詢,通過訊號槽機制,查詢在單獨的執行緒中實現。
  2. 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"