- ???♂? 作者:海碼007
- ?? 專欄:C++專欄
- ?? 標題:【QT HTTP】使用QtNetwork模塊制作基于HTTP請求的C/S架構(gòu)
- ?? 寄語:書到用時方恨少,事非經(jīng)過不知難。
- ?? 最后:文章作者技術(shù)和水平有限,如果文中出現(xiàn)錯誤,希望大家能指正!
0 引言
- 最近項目涉及到網(wǎng)絡(luò)HTTP相關(guān)內(nèi)容,需要處理客戶端發(fā)送的POST、GET等請求。所以本文使用QT的QtNetwork模塊來實現(xiàn)一個簡單的發(fā)送POST、GET請求的客戶端,然后響應(yīng)POST、GET請求的服務(wù)器。
- 本文涉及到一些HTTP的基本知識,可以參考博主之前的文章:計算機網(wǎng)絡(luò)HTTP協(xié)議
HTTP(Hypertext Transfer Protocol)是一種應(yīng)用層協(xié)議,它是建立在傳輸控制協(xié)議(TCP)之上的。在使用HTTP進行客戶端和服務(wù)器之間的通信時,實際上是通過TCP協(xié)議來傳輸HTTP消息。
TCP是一種面向連接的協(xié)議,它提供了可靠的、按順序傳送的字節(jié)流,確保數(shù)據(jù)的完整性和可靠性。HTTP利用TCP的這些特性來確保請求和響應(yīng)的可靠傳輸。
HTTP消息包括請求報文和響應(yīng)報文。請求報文由客戶端發(fā)送給服務(wù)器,而響應(yīng)報文由服務(wù)器發(fā)送給客戶端。這些報文包含了與所請求的資源或所提供的信息相關(guān)的頭部(headers)和實體體(entity body)。
總體而言,HTTP建立在TCP協(xié)議的基礎(chǔ)之上,它定義了一種規(guī)范,用于客戶端和服務(wù)器之間的通信,而TCP則提供了底層的可靠數(shù)據(jù)傳輸機制。
1 HTTP基本知識
1.1 請求類型
HTTP(Hypertext Transfer Protocol)協(xié)議 定義了多種請求方法,也稱為HTTP請求類型或HTTP動詞。這些請求方法表示客戶端希望對特定資源執(zhí)行的操作。以下是常見的HTTP請求類型、其功能和應(yīng)用場景:
-
GET:
- 功能: 用于從服務(wù)器獲取指定資源的信息。請求的參數(shù)通常附在URL后面,通過查詢字符串傳遞。
- 應(yīng)用場景: 用于查看網(wǎng)頁、下載文件、獲取數(shù)據(jù)等。是冪等的,不應(yīng)該對服務(wù)器產(chǎn)生影響。(我們輸入一個網(wǎng)址,其實就是從服務(wù)器獲得一個HTML文件,然后瀏覽器內(nèi)核再根據(jù)其將內(nèi)容繪制出來)
-
POST:
- 功能: 用于向服務(wù)器提交數(shù)據(jù),通常用于表單提交。請求的參數(shù)通常包含在請求體中。
- 應(yīng)用場景: 用于創(chuàng)建新資源、提交表單數(shù)據(jù)、上傳文件等??赡軐Ψ?wù)器產(chǎn)生影響。不是冪等的,多次相同的POST請求可能產(chǎn)生不同的結(jié)果。
-
PUT:
- 功能: 用于向服務(wù)器上傳新資源,或者更新已存在的資源。請求的參數(shù)通常包含在請求體中。
- 應(yīng)用場景: 用于創(chuàng)建或更新資源。是冪等的,多次相同的PUT請求應(yīng)該產(chǎn)生相同的結(jié)果。
-
DELETE:
- 功能: 用于請求服務(wù)器刪除指定的資源。
- 應(yīng)用場景: 用于刪除指定資源。是冪等的,多次相同的DELETE請求應(yīng)該產(chǎn)生相同的結(jié)果。
-
PATCH:
- 功能: 用于對資源進行部分更新。請求的參數(shù)通常包含在請求體中,表示對資源的局部修改。
- 應(yīng)用場景: 用于對資源進行局部更新,而不是替換整個資源。
-
HEAD:
- 功能: 類似于GET請求,但服務(wù)器只返回響應(yīng)頭,不返回實體主體。常用于檢查資源的元信息,如是否存在、是否已經(jīng)修改等。
- 應(yīng)用場景: 用于獲取資源的頭部信息,而不需要獲取整個資源的內(nèi)容。
-
OPTIONS:
- 功能: 用于獲取目標資源支持的通信選項??蛻舳丝梢酝ㄟ^這個方法了解服務(wù)器支持的方法。
- 應(yīng)用場景: 用于確定服務(wù)器支持的方法,以及支持的頭信息等。
-
TRACE:
- 功能: 用于在目標服務(wù)器上執(zhí)行一個消息環(huán)回測試,客戶端發(fā)送的請求會在最終的服務(wù)器上返回,用于診斷和調(diào)試。
- 應(yīng)用場景: 主要用于網(wǎng)絡(luò)診斷,通常不會在實際應(yīng)用中直接使用。
選擇適當(dāng)?shù)腍TTP請求類型取決于具體的操作和業(yè)務(wù)需求。每種請求類型都有其獨特的功能和應(yīng)用場景,使其適用于不同的情境。
1.2 HTTP請求報文格式
HTTP請求報文是客戶端發(fā)送給服務(wù)器的文本信息,包含請求的各種參數(shù)和頭信息。它的基本格式如下:
<Method> <Request-URI> <HTTP-Version>
<Headers>
<Optional Request Body>
其中,各部分的含義如下:
-
<Method>
:HTTP請求方法,例如GET、POST、PUT等。 -
<Request-URI>
:請求的資源標識符,通常是一個URL。 -
<HTTP-Version>
:使用的HTTP協(xié)議版本,例如HTTP/1.1。 -
<Headers>
:包含多行的頭部信息,每行都包含一個頭字段和對應(yīng)的值。 -
<Optional Request Body>
:可選的請求體,用于包含請求時需要發(fā)送的數(shù)據(jù),例如POST請求中的表單數(shù)據(jù)。
以下是一個具體的例子:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1
在這個例子中:
- 請求方法是GET。
- 請求的資源標識符是
/index.html
。 - 使用的HTTP協(xié)議版本是HTTP/1.1。
- 請求頭部包含了
Host
、User-Agent
、Accept
等字段,每個字段都以<header-name>: <header-value>
的形式呈現(xiàn)。 -
由于GET請求通常不包含請求體,因此沒有
<Optional Request Body>
部分。
對于包含請求體的請求,例如POST請求,請求體會緊隨請求頭部,并用一個空行分隔。例如:
POST /submit-form HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:99.0) Gecko/20100101 Firefox/99.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
username=johndoe&password=123
在這個例子中,請求體包含了表單數(shù)據(jù)username=johndoe&password=123
,并通過Content-Type
頭字段指定了數(shù)據(jù)的格式。
1.3 HTTP響應(yīng)報文格式
HTTP響應(yīng)報文是服務(wù)器返回給客戶端的文本信息,包含了服務(wù)器對客戶端請求的響應(yīng)。其基本格式如下:
<HTTP-Version> <Status-Code> <Reason-Phrase>
<Headers>
<Optional Response Body>
其中,各部分的含義如下:
-
<HTTP-Version>
:使用的HTTP協(xié)議版本,例如HTTP/1.1。 -
<Status-Code>
:一個三位數(shù)的狀態(tài)碼,表示服務(wù)器對請求的處理結(jié)果。 -
<Reason-Phrase>
:狀態(tài)碼的文本描述,描述了狀態(tài)碼的原因。 -
<Headers>
:包含多行的頭部信息,每行都包含一個頭字段和對應(yīng)的值。 -
<Optional Response Body>
:可選的響應(yīng)體,用于包含服務(wù)器返回給客戶端的數(shù)據(jù)。
以下是一個具體的例子:
HTTP/1.1 200 OK
Date: Mon, 15 Nov 2023 12:00:00 GMT
Server: Apache
Content-Type: text/html
Content-Length: 1234
Connection: keep-alive
<html>
<head>
<title>Hello, World!</title>
</head>
<body>
<h1>Welcome to my website!</h1>
</body>
</html>
在這個例子中:
- 使用的HTTP協(xié)議版本是HTTP/1.1。
- 狀態(tài)碼是200,表示請求成功。
- 原因短語是"OK",為狀態(tài)碼的文本描述。
- 響應(yīng)頭部包含了
Date
、Server
、Content-Type
等字段。 - 由于這是一個簡單的HTML響應(yīng),響應(yīng)體包含了一個HTML文檔。
對于包含響應(yīng)體的響應(yīng),例如HTML頁面、JSON數(shù)據(jù)等,響應(yīng)體會緊隨響應(yīng)頭部,并用一個空行分隔。響應(yīng)體的格式和內(nèi)容取決于服務(wù)器的實際響應(yīng)。
1.4 拓展:GET vs POST 請求方法
GET和POST請求在HTTP中的請求報文和響應(yīng)報文中有一些區(qū)別,這主要涉及到數(shù)據(jù)的傳遞方式和一些特定的語義約定。
GET請求
請求報文:
-
參數(shù)傳遞: GET請求的參數(shù)通常附在URL的查詢字符串中,通過
?
和&
符號進行連接,例如:http://example.com/resource?param1=value1¶m2=value2
。 - 請求體: GET請求通常沒有請求體,因為它用于請求資源,而不是向服務(wù)器提交數(shù)據(jù)。
響應(yīng)報文
- 響應(yīng)體: GET請求的響應(yīng)體包含了服務(wù)器返回的資源數(shù)據(jù)。
POST請求
請求報文
- 參數(shù)傳遞: POST請求的參數(shù)通常包含在請求體中,而不是在URL中,特別是用于提交表單數(shù)據(jù)或上傳文件等場景。
- 請求體: POST請求的請求體包含了客戶端提交給服務(wù)器的數(shù)據(jù)。
響應(yīng)報文
- 響應(yīng)體: POST請求的響應(yīng)體包含了服務(wù)器對提交的數(shù)據(jù)的處理結(jié)果。
其他注意事項
- 安全性: POST請求的數(shù)據(jù)包含在請求體中,相對于GET請求,POST請求具有更好的安全性,因為它不會在URL中明文傳遞敏感信息。
- 冪等性: GET請求是冪等的,多次相同的GET請求應(yīng)該產(chǎn)生相同的結(jié)果。POST請求是非冪等的,多次相同的POST請求可能會產(chǎn)生不同的結(jié)果。
示例:
GET請求示例
請求報文:
GET /resource?param1=value1¶m2=value2 HTTP/1.1
Host: example.com
響應(yīng)報文:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<!DOCTYPE html>
<html>
<head>
<title>GET Response</title>
</head>
<body>
<h1>This is the response to a GET request.</h1>
</body>
</html>
POST請求示例
請求報文:
POST /submit-form HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
param1=value1¶m2=value2
響應(yīng)報文:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45
{"status": "success", "message": "POST response"}
總的來說,GET和POST請求的區(qū)別主要在于參數(shù)傳遞的方式、請求體的內(nèi)容和請求的語義。GET適用于獲取資源,而POST適用于向服務(wù)器提交數(shù)據(jù)。
2 實戰(zhàn)
2.1 QtNetwork模塊介紹
QtNetwork模塊是Qt中用于網(wǎng)絡(luò)編程的模塊,提供了一系列用于處理網(wǎng)絡(luò)通信的類和工具。以下是QtNetwork模塊的一些主要功能:
-
TCP和UDP通信: 提供
QTcpSocket
和QUdpSocket
等類,用于實現(xiàn)TCP和UDP協(xié)議的通信。這些類使得在Qt應(yīng)用程序中創(chuàng)建和管理網(wǎng)絡(luò)連接變得相對簡單。 -
HTTP客戶端和服務(wù)器: 提供
QNetworkAccessManager
類,用于實現(xiàn)HTTP協(xié)議的客戶端功能。它支持GET、POST等HTTP請求方法,并允許異步地發(fā)送和接收HTTP請求。 -
網(wǎng)絡(luò)請求和響應(yīng)處理: 提供
QNetworkRequest
和QNetworkReply
等類,用于構(gòu)建和處理網(wǎng)絡(luò)請求。這些類提供了豐富的功能,包括請求頭的設(shè)置、數(shù)據(jù)的傳輸和響應(yīng)的處理等。 -
FTP客戶端: 提供
QFtp
類,用于實現(xiàn)FTP協(xié)議的客戶端功能。它允許在Qt應(yīng)用程序中進行文件傳輸操作。 -
網(wǎng)絡(luò)代理: 支持網(wǎng)絡(luò)代理設(shè)置,可以通過
QNetworkProxy
類配置網(wǎng)絡(luò)代理,以便在需要時通過代理服務(wù)器進行網(wǎng)絡(luò)通信。 -
網(wǎng)絡(luò)協(xié)議支持: QtNetwork模塊支持各種網(wǎng)絡(luò)協(xié)議,包括IPv4和IPv6,SSL/TLS等。這使得Qt應(yīng)用程序能夠適應(yīng)多種網(wǎng)絡(luò)環(huán)境和安全需求。
-
網(wǎng)絡(luò)狀態(tài)監(jiān)控: 提供
QNetworkConfiguration
和QNetworkConfigurationManager
類,用于監(jiān)控和管理網(wǎng)絡(luò)配置,以便在應(yīng)用程序中適應(yīng)不同的網(wǎng)絡(luò)狀態(tài)。 -
網(wǎng)絡(luò)緩存: 提供
QNetworkDiskCache
等類,用于實現(xiàn)網(wǎng)絡(luò)緩存,以提高應(yīng)用程序的性能并減少對網(wǎng)絡(luò)資源的依賴。
這些功能使QtNetwork成為一個強大的網(wǎng)絡(luò)編程工具,適用于開發(fā)涉及網(wǎng)絡(luò)通信的各種應(yīng)用,從簡單的客戶端到復(fù)雜的服務(wù)器應(yīng)用。
2.2 編程實現(xiàn)HTTP客戶端
根據(jù)上述描述,可以知道,使用 QTcpSocket
和QUdpSocket
、QNetworkAccessManager
、QNetworkRequest
和QNetworkReply
等類可以實現(xiàn)簡單的HTTP客戶端。
- (本實例只是制作了一個簡單的 HTTP 客戶端,并不能持久的連接 HTTP 服務(wù)器。類似于瀏覽器給服務(wù)器發(fā)出HTTP請求)接下來是代碼:
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
#include <QUrlQuery>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 創(chuàng)建網(wǎng)絡(luò)訪問管理器
QNetworkAccessManager manager;
// 創(chuàng)建HTTP請求
QNetworkRequest getRequest(QUrl("http://example.com"));
// 發(fā)送GET請求
QNetworkReply *getReply = manager.get(getRequest);
// 處理GET請求完成的信號
QObject::connect(getReply, &QNetworkReply::finished, [&]() {
if (getReply->error() == QNetworkReply::NoError) {
qDebug() << "GET Response:" << getReply->readAll();
} else {
qDebug() << "GET Error:" << getReply->errorString();
}
getReply->deleteLater();
});
// 進入應(yīng)用程序事件循環(huán)
return a.exec();
}
在使用 Qt 進行網(wǎng)絡(luò)請求時,尤其是在進行異步的網(wǎng)絡(luò)操作時,需要進入應(yīng)用程序的事件循環(huán)。這是因為 Qt 的事件循環(huán)負責(zé)處理事件,而網(wǎng)絡(luò)請求的完成(比如接收到服務(wù)器的響應(yīng))通常是通過 Qt 的信號-槽機制來處理的。
讓我們來詳細解釋一下:
-
異步操作: Qt 的網(wǎng)絡(luò)操作通常是異步的,即在發(fā)起網(wǎng)絡(luò)請求后,程序會繼續(xù)執(zhí)行后續(xù)的代碼而不等待請求完成。這是為了確保應(yīng)用程序的界面和其他部分能夠保持響應(yīng)性,不被阻塞。
-
信號-槽機制: 當(dāng)網(wǎng)絡(luò)請求完成時,
QNetworkReply
會發(fā)出finished
信號。你在代碼中使用QObject::connect
來連接這個信號到一個槽函數(shù),以便在請求完成時執(zhí)行一些操作。 -
事件循環(huán): 為了讓信號-槽機制正常工作,需要進入應(yīng)用程序的事件循環(huán)。調(diào)用
QCoreApplication::exec()
或者QEventLoop::exec()
啟動事件循環(huán),使得 Qt 可以不斷地檢查并處理事件隊列。
在代碼中,調(diào)用 return a.exec();
啟動了事件循環(huán)。這樣,當(dāng) GET
請求完成并發(fā)出 finished
信號時,相關(guān)的槽函數(shù)將會被執(zhí)行。如果沒有進入事件循環(huán),這個槽函數(shù)將不會被觸發(fā),因為事件循環(huán)負責(zé)調(diào)度信號的處理。
簡而言之,進入應(yīng)用程序的事件循環(huán)是確保異步操作和信號-槽機制正常工作的關(guān)鍵步驟。如果你的應(yīng)用程序沒有事件循環(huán),它將無法及時響應(yīng)和處理異步操作的完成事件。
- 示例2:使用TCP Socket制作一個 HTTP 客戶端
基本功能:連接服務(wù)器、發(fā)送請求報文、接受響應(yīng)報文
.h文件代碼:
// httpclient.h
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H
#include <QTcpSocket>
#include <QObject>
class HttpClient : public QObject
{
Q_OBJECT
public:
// 構(gòu)造函數(shù)
explicit HttpClient(QObject *parent = nullptr);
public slots:
// 連接服務(wù)器
void connectToServer();
private slots:
// 讀取服務(wù)器響應(yīng)報文槽函數(shù)
void readServerResponse();
private:
// 服務(wù)器Socket
QTcpSocket *tcpSocket;
};
#endif // HTTPCLIENT_H
.cpp文件代碼:
// httpclient.cpp
#include "httpclient.h"
HttpClient::HttpClient(QObject *parent)
: QObject(parent)
{
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::connected, this, &HttpClient::readServerResponse);
}
void HttpClient::connectToServer()
{
tcpSocket->connectToHost("127.0.0.1", 8080);
}
void HttpClient::readServerResponse()
{
QByteArray response = tcpSocket->readAll();
qDebug() << "Received response:\n" << response;
tcpSocket->close();
}
2.3 編程實現(xiàn)HTTP服務(wù)器
QNetworkAccessManager
主要用于客戶端,用于處理發(fā)起 HTTP 請求。對于服務(wù)器端,通常需要使用 QTcpServer
和 QTcpSocket
等類來監(jiān)聽連接并處理請求。在下面的例子中,我將演示如何使用 QTcpServer
處理簡單的 HTTP GET
和 POST
請求。
下面的是使用 QT 的 Tcp Socket
實現(xiàn)的 HTTP
服務(wù)器功能,HTTP
協(xié)議是基于 Tcp
協(xié)議實現(xiàn)的,所以 HTTP 通信的本質(zhì)還是 C/S 之間進行 Tcp Socket
通信。通過字節(jié)流的方式發(fā)送和接受數(shù)據(jù)。無非就是 HTTP 發(fā)送的是 HTTP 格式的請求報文
和響應(yīng)報文
。所以在 Tcp 層次也就是網(wǎng)絡(luò)層次接受到的是報文的字節(jié)流數(shù)據(jù)。HTTP服務(wù)器只需要對接受到的請求報文進行解析即可。
因為請求報文的格式是統(tǒng)一的規(guī)范大家都遵守,所以讀取傳輸數(shù)據(jù)的第一行數(shù)據(jù)的第一個字符串即可知道使用的請求方法是GET
、POST
等。然后根據(jù)不同的請求方法,寫好不同的處理函數(shù)即可。并寫好對應(yīng)格式的響應(yīng)報文即可。
當(dāng)然我們也可以使用
#ifndef MYHTTPSERVER_H
#define MYHTTPSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QUrlQuery>
#include <QDebug>
class MyHTTPServer : public QTcpServer
{
Q_OBJECT
public:
MyHTTPServer(QObject *parent = nullptr) : QTcpServer(parent) {}
protected:
//--------------------------------------
// 說明:這是 QTcpServer 類的虛函數(shù),當(dāng)有新的連接到達時,會被調(diào)用。
// 日期:2023-11-15
// 作者:海碼007
//--------------------------------------
void incomingConnection(qintptr socketDescriptor) override
{
QTcpSocket *socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescriptor);
// 讀取客戶端請求
connect(socket, &QTcpSocket::readyRead, [&]() {
QByteArray requestData = socket->readAll();
processRequest(requestData, socket);
// 關(guān)閉連接
socket->disconnectFromHost();
});
// 處理連接斷開
connect(socket, &QTcpSocket::disconnected, [&]() {
socket->deleteLater();
});
}
private:
//--------------------------------------
// 說明:這個函數(shù)用于解析 HTTP 請求,分析請求的方法和路徑,并調(diào)用相應(yīng)的處理函數(shù)。
// 日期:2023-11-15
// 作者:海碼007
//--------------------------------------
void processRequest(const QByteArray &requestData, QTcpSocket *socket)
{
// 解析請求
QString requestString = QString::fromUtf8(requestData);
QStringList requestLines = requestString.split("\r\n");
// 解析第一行,獲取請求方法和路徑
QString firstLine = requestLines.first();
QStringList parts = firstLine.split(" ");
QString method = parts.value(0);
QString path = parts.value(1);
// 處理 GET 請求
if (method == "GET")
{
handleGetRequest(path, socket);
}
// 處理 POST 請求
else if (method == "POST")
{
handlePostRequest(path, requestData, socket);
}
}
//--------------------------------------
// 說明:處理 HTTP GET 請求的具體邏輯。
// 日期:2023-11-15
// 作者:海碼007
//--------------------------------------
void handleGetRequest(const QString &path, QTcpSocket *socket)
{
QTextStream responseStream(socket);
responseStream.setAutoDetectUnicode(true);
// 構(gòu)造HTTP響應(yīng)
responseStream << "HTTP/1.1 200 OK\r\n"
<< "Content-Type: text/html\r\n"
<< "Connection: close\r\n"
<< "\r\n"
<< "<html><body><h1>Hello, World! (GET)</h1></body></html>";
// 刷新并等待數(shù)據(jù)發(fā)送完畢
socket->flush();
socket->waitForBytesWritten();
}
//--------------------------------------
// 說明:處理 HTTP POST 請求的具體邏輯。
// 日期:2023-11-15
// 作者:海碼007
//--------------------------------------
void handlePostRequest(const QString &path, const QByteArray &requestData, QTcpSocket *socket)
{
// 解析 POST 數(shù)據(jù)
QUrlQuery postData(requestData);
QString value = postData.queryItemValue("key");
QTextStream responseStream(socket);
responseStream.setAutoDetectUnicode(true);
// 構(gòu)造HTTP響應(yīng)
responseStream << "HTTP/1.1 200 OK\r\n"
<< "Content-Type: text/html\r\n"
<< "Connection: close\r\n"
<< "\r\n"
<< "<html><body><h1>Hello, " << value << "! (POST)</h1></body></html>";
// 刷新并等待數(shù)據(jù)發(fā)送完畢
socket->flush();
socket->waitForBytesWritten();
}
};
#endif // MYHTTPSERVER_H
擴展:另一個簡單示例
.h文件示例:文章來源:http://www.zghlxwxcb.cn/news/detail-775624.html
// httpserver.h
#ifndef HTTPSERVER_H
#define HTTPSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QObject>
class HttpServer : public QTcpServer
{
Q_OBJECT
public:
// 構(gòu)造函數(shù)
explicit HttpServer(QObject *parent = nullptr);
protected:
// 當(dāng)有客戶端連接到本服務(wù)器時執(zhí)行的函數(shù),這個函數(shù)是父類的函數(shù),需要重寫
void incomingConnection(qintptr socketDescriptor) override;
private slots:
// 讀取客戶端發(fā)送的請求報文
void readClient();
private:
// 給客戶端發(fā)送響應(yīng)報文
void sendResponse(QTcpSocket *clientSocket, const QByteArray &response);
};
#endif // HTTPSERVER_H
cpp文件示例:文章來源地址http://www.zghlxwxcb.cn/news/detail-775624.html
// httpserver.cpp
#include "httpserver.h"
HttpServer::HttpServer(QObject *parent)
: QTcpServer(parent)
{
}
void HttpServer::incomingConnection(qintptr socketDescriptor)
{
// 創(chuàng)建一個 connect Socekt 用于存儲客戶端和服務(wù)器的IP和端口,是個四元組數(shù)據(jù)
QTcpSocket *clientSocket = new QTcpSocket(this);
clientSocket->setSocketDescriptor(socketDescriptor);
// 新建一個信號槽連接,當(dāng) connect Socket 準備好數(shù)據(jù)讀取時的信號,與讀取槽函數(shù)連接
connect(clientSocket, &QTcpSocket::readyRead, this, &HttpServer::readClient);
}
// 讀取客戶端請求報文的槽函數(shù)
void HttpServer::readClient()
{
// sender()函數(shù)返回的指針是信號發(fā)送者的指針
QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());
if (!clientSocket)
return;
// 發(fā)送的請求報文存儲在 connect Socket 中直接讀取即可。數(shù)據(jù)是以字節(jié)流的方式傳輸,如果要正確打印,需要進行解碼
QByteArray requestData = clientSocket->readAll();
qDebug() << "Received request:\n" << requestData;
// 構(gòu)造HTTP響應(yīng)
QByteArray response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!";
// 通過 connect Socket給客戶端發(fā)送響應(yīng)報文
sendResponse(clientSocket, response);
clientSocket->close();
}
void HttpServer::sendResponse(QTcpSocket *clientSocket, const QByteArray &response)
{
clientSocket->write(response);
}
到了這里,關(guān)于【QT HTTP】使用QtNetwork模塊制作基于HTTP請求的C/S架構(gòu)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!