上海哪家做网站关键词排名,如何做简洁网站设计,群晖套件做网站,如何提高网站文章收录目录 0 引言1 HTTP基本知识1.1 请求类型1.2 HTTP请求报文格式1.3 HTTP响应报文格式1.4 拓展#xff1a;GET vs POST 请求方法GET请求请求报文#xff1a;响应报文 POST请求请求报文响应报文 其他注意事项示例#xff1a;GET请求示例POST请求示例 2 实战2.1 QtNetwork模块介绍… 目录 0 引言1 HTTP基本知识1.1 请求类型1.2 HTTP请求报文格式1.3 HTTP响应报文格式1.4 拓展GET vs POST 请求方法GET请求请求报文响应报文 POST请求请求报文响应报文 其他注意事项示例GET请求示例POST请求示例 2 实战2.1 QtNetwork模块介绍2.2 编程实现HTTP客户端2.3 编程实现HTTP服务器 ♂️ 作者海码007 专栏C专栏 标题【QT HTTP】使用QtNetwork模块制作基于HTTP请求的C/S架构❣️ 寄语书到用时方恨少事非经过不知难。 最后文章作者技术和水平有限如果文中出现错误希望大家能指正 0 引言
最近项目涉及到网络HTTP相关内容需要处理客户端发送的POST、GET等请求。所以本文使用QT的QtNetwork模块来实现一个简单的发送POST、GET请求的客户端然后响应POST、GET请求的服务器。本文涉及到一些HTTP的基本知识可以参考博主之前的文章计算机网络HTTP协议
1 HTTP基本知识
1.1 请求类型
HTTPHypertext Transfer Protocol协议 定义了多种请求方法也称为HTTP请求类型或HTTP动词。这些请求方法表示客户端希望对特定资源执行的操作。以下是常见的HTTP请求类型、其功能和应用场景 GET 功能 用于从服务器获取指定资源的信息。请求的参数通常附在URL后面通过查询字符串传递。应用场景 用于查看网页、下载文件、获取数据等。是幂等的不应该对服务器产生影响。我们输入一个网址其实就是从服务器获得一个HTML文件然后浏览器内核再根据其将内容绘制出来 POST 功能 用于向服务器提交数据通常用于表单提交。请求的参数通常包含在请求体中。应用场景 用于创建新资源、提交表单数据、上传文件等。可能对服务器产生影响。不是幂等的多次相同的POST请求可能产生不同的结果。 PUT 功能 用于向服务器上传新资源或者更新已存在的资源。请求的参数通常包含在请求体中。应用场景 用于创建或更新资源。是幂等的多次相同的PUT请求应该产生相同的结果。 DELETE 功能 用于请求服务器删除指定的资源。应用场景 用于删除指定资源。是幂等的多次相同的DELETE请求应该产生相同的结果。 PATCH 功能 用于对资源进行部分更新。请求的参数通常包含在请求体中表示对资源的局部修改。应用场景 用于对资源进行局部更新而不是替换整个资源。 HEAD 功能 类似于GET请求但服务器只返回响应头不返回实体主体。常用于检查资源的元信息如是否存在、是否已经修改等。应用场景 用于获取资源的头部信息而不需要获取整个资源的内容。 OPTIONS 功能 用于获取目标资源支持的通信选项。客户端可以通过这个方法了解服务器支持的方法。应用场景 用于确定服务器支持的方法以及支持的头信息等。 TRACE 功能 用于在目标服务器上执行一个消息环回测试客户端发送的请求会在最终的服务器上返回用于诊断和调试。应用场景 主要用于网络诊断通常不会在实际应用中直接使用。
选择适当的HTTP请求类型取决于具体的操作和业务需求。每种请求类型都有其独特的功能和应用场景使其适用于不同的情境。
1.2 HTTP请求报文格式
HTTP请求报文是客户端发送给服务器的文本信息包含请求的各种参数和头信息。它的基本格式如下
Method Request-URI HTTP-Version
HeadersOptional Request Body其中各部分的含义如下
MethodHTTP请求方法例如GET、POST、PUT等。Request-URI请求的资源标识符通常是一个URL。HTTP-Version使用的HTTP协议版本例如HTTP/1.1。Headers包含多行的头部信息每行都包含一个头字段和对应的值。Optional Request Body可选的请求体用于包含请求时需要发送的数据例如POST请求中的表单数据。
以下是一个具体的例子
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/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8
Accept-Language: en-US,en;q0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1在这个例子中
请求方法是GET。请求的资源标识符是/index.html。使用的HTTP协议版本是HTTP/1.1。请求头部包含了Host、User-Agent、Accept等字段每个字段都以header-name: header-value的形式呈现。由于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: 23usernamejohndoepassword123在这个例子中请求体包含了表单数据usernamejohndoepassword123并通过Content-Type头字段指定了数据的格式。
1.3 HTTP响应报文格式
HTTP响应报文是服务器返回给客户端的文本信息包含了服务器对客户端请求的响应。其基本格式如下
HTTP-Version Status-Code Reason-Phrase
HeadersOptional Response Body其中各部分的含义如下
HTTP-Version使用的HTTP协议版本例如HTTP/1.1。Status-Code一个三位数的状态码表示服务器对请求的处理结果。Reason-Phrase状态码的文本描述描述了状态码的原因。Headers包含多行的头部信息每行都包含一个头字段和对应的值。Optional Response Body可选的响应体用于包含服务器返回给客户端的数据。
以下是一个具体的例子
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-alivehtmlheadtitleHello, World!/title/headbodyh1Welcome to my website!/h1/body
/html在这个例子中
使用的HTTP协议版本是HTTP/1.1。状态码是200表示请求成功。原因短语是OK为状态码的文本描述。响应头部包含了Date、Server、Content-Type等字段。由于这是一个简单的HTML响应响应体包含了一个HTML文档。
对于包含响应体的响应例如HTML页面、JSON数据等响应体会紧随响应头部并用一个空行分隔。响应体的格式和内容取决于服务器的实际响应。
1.4 拓展GET vs POST 请求方法
GET和POST请求在HTTP中的请求报文和响应报文中有一些区别这主要涉及到数据的传递方式和一些特定的语义约定。 GET请求
请求报文
参数传递 GET请求的参数通常附在URL的查询字符串中通过?和符号进行连接例如http://example.com/resource?param1value1param2value2。请求体 GET请求通常没有请求体因为它用于请求资源而不是向服务器提交数据。
响应报文
响应体 GET请求的响应体包含了服务器返回的资源数据。 POST请求
请求报文
参数传递 POST请求的参数通常包含在请求体中而不是在URL中特别是用于提交表单数据或上传文件等场景。请求体 POST请求的请求体包含了客户端提交给服务器的数据。 响应报文
响应体 POST请求的响应体包含了服务器对提交的数据的处理结果。
其他注意事项
安全性 POST请求的数据包含在请求体中相对于GET请求POST请求具有更好的安全性因为它不会在URL中明文传递敏感信息。幂等性 GET请求是幂等的多次相同的GET请求应该产生相同的结果。POST请求是非幂等的多次相同的POST请求可能会产生不同的结果。 示例
GET请求示例
请求报文
GET /resource?param1value1param2value2 HTTP/1.1
Host: example.com响应报文
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234!DOCTYPE html
htmlheadtitleGET Response/title/headbodyh1This is the response to a GET request./h1/body
/htmlPOST请求示例
请求报文
POST /submit-form HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27param1value1param2value2响应报文
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45{status: success, message: POST response}总的来说GET和POST请求的区别主要在于参数传递的方式、请求体的内容和请求的语义。GET适用于获取资源而POST适用于向服务器提交数据。
2 实战
2.1 QtNetwork模块介绍
QtNetwork模块是Qt中用于网络编程的模块提供了一系列用于处理网络通信的类和工具。以下是QtNetwork模块的一些主要功能 TCP和UDP通信 提供QTcpSocket和QUdpSocket等类用于实现TCP和UDP协议的通信。这些类使得在Qt应用程序中创建和管理网络连接变得相对简单。 HTTP客户端和服务器 提供QNetworkAccessManager类用于实现HTTP协议的客户端功能。它支持GET、POST等HTTP请求方法并允许异步地发送和接收HTTP请求。 网络请求和响应处理 提供QNetworkRequest和QNetworkReply等类用于构建和处理网络请求。这些类提供了丰富的功能包括请求头的设置、数据的传输和响应的处理等。 FTP客户端 提供QFtp类用于实现FTP协议的客户端功能。它允许在Qt应用程序中进行文件传输操作。 网络代理 支持网络代理设置可以通过QNetworkProxy类配置网络代理以便在需要时通过代理服务器进行网络通信。 网络协议支持 QtNetwork模块支持各种网络协议包括IPv4和IPv6SSL/TLS等。这使得Qt应用程序能够适应多种网络环境和安全需求。 网络状态监控 提供QNetworkConfiguration和QNetworkConfigurationManager类用于监控和管理网络配置以便在应用程序中适应不同的网络状态。 网络缓存 提供QNetworkDiskCache等类用于实现网络缓存以提高应用程序的性能并减少对网络资源的依赖。
这些功能使QtNetwork成为一个强大的网络编程工具适用于开发涉及网络通信的各种应用从简单的客户端到复杂的服务器应用。
2.2 编程实现HTTP客户端
根据上述描述可以知道使用 QTcpSocket和QUdpSocket、QNetworkAccessManager、QNetworkRequest和QNetworkReply等类可以实现简单的HTTP客户端。
接下来是代码
#include QCoreApplication
#include QNetworkAccessManager
#include QNetworkRequest
#include QNetworkReply
#include QDebug
#include QUrlQueryint main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 创建网络访问管理器QNetworkAccessManager manager;// 创建HTTP请求QNetworkRequest getRequest(QUrl(http://example.com));// 发送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();});// 进入应用程序事件循环return a.exec();
}在使用 Qt 进行网络请求时尤其是在进行异步的网络操作时需要进入应用程序的事件循环。这是因为 Qt 的事件循环负责处理事件而网络请求的完成比如接收到服务器的响应通常是通过 Qt 的信号-槽机制来处理的。
让我们来详细解释一下 异步操作 Qt 的网络操作通常是异步的即在发起网络请求后程序会继续执行后续的代码而不等待请求完成。这是为了确保应用程序的界面和其他部分能够保持响应性不被阻塞。 信号-槽机制 当网络请求完成时QNetworkReply 会发出 finished 信号。你在代码中使用 QObject::connect 来连接这个信号到一个槽函数以便在请求完成时执行一些操作。 事件循环 为了让信号-槽机制正常工作需要进入应用程序的事件循环。调用 QCoreApplication::exec() 或者 QEventLoop::exec() 启动事件循环使得 Qt 可以不断地检查并处理事件队列。
在代码中调用 return a.exec(); 启动了事件循环。这样当 GET 请求完成并发出 finished 信号时相关的槽函数将会被执行。如果没有进入事件循环这个槽函数将不会被触发因为事件循环负责调度信号的处理。
简而言之进入应用程序的事件循环是确保异步操作和信号-槽机制正常工作的关键步骤。如果你的应用程序没有事件循环它将无法及时响应和处理异步操作的完成事件。
2.3 编程实现HTTP服务器
#ifndef MYHTTPSERVER_H
#define MYHTTPSERVER_H#include QTcpServer
#include QTcpSocket
#include QUrlQuery
#include QDebugclass MyHTTPServer : public QTcpServer
{Q_OBJECTpublic:MyHTTPServer(QObject *parent nullptr) : QTcpServer(parent) {}protected://--------------------------------------// 说明这是 QTcpServer 类的虚函数当有新的连接到达时会被调用。// 日期2023-11-15// 作者何浩文//--------------------------------------void incomingConnection(qintptr socketDescriptor) override{QTcpSocket *socket new QTcpSocket(this);socket-setSocketDescriptor(socketDescriptor);// 读取客户端请求connect(socket, QTcpSocket::readyRead, []() {QByteArray requestData socket-readAll();processRequest(requestData, socket);// 关闭连接socket-disconnectFromHost();});// 处理连接断开connect(socket, QTcpSocket::disconnected, []() {socket-deleteLater();});}private://--------------------------------------// 说明这个函数用于解析 HTTP 请求分析请求的方法和路径并调用相应的处理函数。// 日期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);// 构造HTTP响应responseStream HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n Connection: close\r\n \r\n htmlbodyh1Hello, World! (GET)/h1/body/html;// 刷新并等待数据发送完毕socket-flush();socket-waitForBytesWritten();}//--------------------------------------// 说明处理 HTTP POST 请求的具体逻辑。// 日期2023-11-15// 作者海码007//--------------------------------------void handlePostRequest(const QString path, const QByteArray requestData, QTcpSocket *socket){// 解析 POST 数据QUrlQuery postData(requestData);QString value postData.queryItemValue(key);QTextStream responseStream(socket);responseStream.setAutoDetectUnicode(true);// 构造HTTP响应responseStream HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n Connection: close\r\n \r\n htmlbodyh1Hello, value ! (POST)/h1/body/html;// 刷新并等待数据发送完毕socket-flush();socket-waitForBytesWritten();}
};#endif // MYHTTPSERVER_H