
QT_HTTP协议编程http超文本传输协议是一个简单的请求响应协议它通常运行在TCP之上作用规定WWW服务器与浏览器之间信息传递规范是二者共同遵守的协议。http工作原理http是基于客户/服务器模式且面向连接的http事务处理流程客户与服务器建立连接客户想服务器提出请求服务器接受请求并根据请求返回相应的文件作为应答客户与服务器关闭连接HTTP 报文结构补充HTTP 协议在客户端与服务器之间传递的数据统称为「报文」分为请求报文和响应报文两大类。二者结构相似都由四部分组成1. 请求报文Request请求行方法 URL 版本 请求头若干 key: value 键值对 空行\r\n\r\n用于分隔头部与正文 请求体可选如 POST 提交的数据示例GET 请求GET /index.html HTTP/1.1 Host: www.baidu.com User-Agent: Mozilla/5.0 Accept: text/html Connection: keep-alive2. 响应报文Response状态行版本 状态码 原因短语 响应头 空行 响应体服务器返回的实际内容示例对应项目二的服务器返回HTTP/1.1 200 OK Content-Type: text/html Connection: close Refresh: 1 !DOCTYPEhtml.../html3. 常用请求方法Method方法含义是否带请求体GET请求获取指定资源数据附在 URL 上否POST向服务器提交数据数据在请求体中是PUT上传/更新指定资源是DELETE删除指定资源否HEAD只获取响应头不返回正文否4. 常见状态码Status Code状态码含义200OK请求成功项目二中用到301/302重定向400请求有误语法错误403禁止访问404资源未找到500服务器内部错误5. 常见请求头/响应头补充Host请求的目标主机名HTTP/1.1 必带。User-Agent标识客户端类型浏览器/程序。Content-Type声明正文数据的类型如text/html、application/json。Content-Length正文的字节数。Connectionkeep-alive保持连接或close关闭连接。Refresh让浏览器在指定秒数后自动刷新页面项目二中用到。相关APIQt 中进行 HTTP 编程的核心类是QNetworkAccessManager它负责发送网络请求并接收响应封装了底层的 TCP 细节。常用的相关类如下1. QNetworkAccessManager网络访问管理器HTTP 编程的「总调度」一个程序中通常只需创建一个实例可复用。主要方法get(请求)发送一次GET请求返回一个QNetworkReply对象。post(请求, 数据)发送一次POST请求。put(请求, 数据)上传数据。deleteResource(请求)发送 DELETE 请求。注意以上方法都是异步的调用后会立刻返回真正的响应通过信号finished(QNetworkReply*)通知。2. QNetworkRequest网络请求对象用来描述一次「请求」的详细信息目标地址、请求头等。通常结合QUrl使用QNetworkRequestreq(QUrl(http://www.baidu.com));req.setHeader(QNetworkRequest::UserAgentHeader,MyQtApp/1.0);// 设置请求头3. QNetworkReply网络应答对象继承自QIODevice表示一次请求的「响应」。每次请求都会生成一个新的 reply 对象用完需要释放。常用成员readAll()读取服务器返回的全部数据QByteArray。error()获取错误信息。attribute(QNetworkRequest::HttpStatusCodeAttribute)读取 HTTP 状态码。4. QUrl统一资源定位符用于解析、构造 URL是网络请求的地址载体。5. 关键信号与槽QNetworkAccessManager::finished(QNetworkReply*)当一个请求完成后触发无论成功或失败都会发出。典型用法在槽函数里readAll()读取数据 → 处理 →reply-deleteLater()释放。HTTP项目实战一项目简介补充本实战使用QNetworkAccessManager实现一个GET 请求客户端点击按钮后向指定 URL 发起请求把服务器返回的内容显示到界面上并提示下载状态。工程配置补充.pro 文件Qt 5 及以前版本使用QT networkQt 6 无需额外模块网络模块已默认包含但加上也不报错。示例如下QT core gui network greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET HttpGetDemo TEMPLATE app SOURCES \ main.cpp \ mainwindow.cpp HEADERS \ mainwindow.h FORMS \ mainwindow.ui关键点必须添加network模块否则会在链接阶段报QNetworkAccessManager未定义等错误。界面布局补充在 UI 设计器中拖入以下控件即可对应代码中的对象名一个QPushButtonobjectName 改为getDataBtn文字如「获取数据」。一个QLabelobjectName 为label用于显示下载状态。一个QTextBrowser或QPlainTextEditobjectName 为textBrowser用于显示返回内容。代码文件mainwindow.h#ifndefMAINWINDOW_H#defineMAINWINDOW_H#includeQMainWindow#includeQtNetwork// 提供编程Tcp/IP客户端和服务器的类#includeQUrl// 提供接口使用URLsclassQNetworkAccessManager;classQNetworkReply;// 此类是QIODevice的子类补充原注释 “QIDevice” 应为 QIODeviceQT_BEGIN_NAMESPACEnamespaceUi{classMainWindow;}QT_END_NAMESPACEclassMainWindow:publicQMainWindow{Q_OBJECTpublic:MainWindow(QWidget*parentnullptr);~MainWindow();private:Ui::MainWindow*ui;QNetworkAccessManager*mgr;publicslots:voidreplayFinishedFunc(QNetworkReply*);//响应补充函数名语义应为 replyFinishedFunc与信号 finished 对应privateslots:voidon_getDataBtn_clicked();};#endif// MAINWINDOW_Hmainwindow.cpp#includemainwindow.h#includeui_mainwindow.hMainWindow::MainWindow(QWidget*parent):QMainWindow(parent),ui(newUi::MainWindow){ui-setupUi(this);mgrnewQNetworkAccessManager(this);connect(mgr,SIGNAL(finished(QNetworkReply*)),this,SLOT(replayFinishedFunc(QNetworkReply*)));}MainWindow::~MainWindow(){deleteui;}voidMainWindow::replayFinishedFunc(QNetworkReply*reply){QString strallreply-readAll();//读取数据ui-textBrowser-setText(strall);// 显示数据ui-label-setText(下载完成);reply-deleteLater();}// 以下为补充的优化点与扩展代码 // 补充 1错误处理。上面的代码无论请求成功与否都显示「下载完成」// 实际项目中建议根据 reply-error() 判断避免访问失败时误导用户。// 可改进为/* void MainWindow::replayFinishedFunc(QNetworkReply *reply){ if (reply-error() QNetworkReply::NoError) { QString strall reply-readAll(); ui-textBrowser-setText(strall); ui-label-setText(下载完成); } else { ui-label-setText(下载失败 reply-errorString()); } reply-deleteLater(); } */// 补充 2获取 HTTP 状态码在 readAll 之前读取。/* QVariant statusCode reply-attribute(QNetworkRequest::HttpStatusCodeAttribute); if (statusCode.isValid()) { qDebug() 状态码: statusCode.toInt(); } */voidMainWindow::on_getDataBtn_clicked(){ui-label-setText(资源正在下载中请耐心等待...);mgr-get(QNetworkRequest(QUrl(http://www.baidu.com)));}运行说明补充点击「获取数据」按钮程序会异步向http://www.baidu.com发起 GET 请求。请求发出后label显示「资源正在下载中请耐心等待…」。当finished信号触发后replayFinishedFunc槽函数把返回的网页源码显示在textBrowser中并把label改为「下载完成」。关键点小结补充QNetworkAccessManager只需 new 一次在窗口生命周期内复用即可父对象设为this会自动释放。mgr-get()是异步操作不会阻塞界面真正的结果通过finished信号异步返回。槽函数末尾必须调用reply-deleteLater()释放 reply 对象避免内存泄漏。连接信号槽时新式写法QNetworkAccessManager::finished比字符串SIGNAL/SLOT更安全可被编译器检查。HTTP项目实战二项目简介补充本实战不依赖QNetworkAccessManager而是直接基于QTcpServer/QTcpSocket手写一个极简 HTTP 服务器监听 8080 端口当浏览器访问时返回一段带自动刷新每秒 1计数的 HTML 页面。通过这个例子可以直观看到 HTTP 协议「底层就是 TCP 文本报文」的本质——响应报文是直接拼字符串再 write 出去的。工程配置补充.pro 文件因为是控制台程序QCoreApplication无需widgets模块QT core network QT - gui CONFIG console CONFIG - app_bundle TARGET MiniHttpServer TEMPLATE app SOURCES \ main.cpp \ myhttpserver.cpp HEADERS \ myhttpserver.h运行与测试方式补充编译运行程序控制台输出「正常启动web服务器端口8080」。打开任意浏览器访问http://localhost:8080或本机 IP 加端口。页面显示「客户端已经连接到httpServer服务器秒数为N」且因响应头含Refresh: 1页面每秒自动刷新、数字每秒递增。代码文件main.cpp/** * file main.cpp * brief 程序入口文件 * * 这是一个基于 Qt 控制台无 GUI的极简 HTTP Web 服务器示例程序。 * 程序启动后会创建 myHttpServer 对象监听 8080 端口 * 当浏览器访问 http://localhost:8080 时返回一段显示访问计数的 HTML 页面。 */#includeQCoreApplication// 控制台应用程序类不需要 GUI#includemyhttpserver.h// 自定义的 HTTP 服务器类// 补充说明 // 与项目一不同这里不使用 QNetworkAccessManager// 而是用 QTcpServer 直接监听端口、手写 HTTP 响应报文。// 本质HTTP 请求/响应就是基于 TCP 传输的文本格式报文。/** * brief 程序入口函数 * param argc 命令行参数个数 * param argv 命令行参数数组 * return 程序退出码 */intmain(intargc,char*argv[]){// 创建 Qt 控制台应用程序对象// 该对象负责管理程序的事件循环等基础功能QCoreApplicationa(argc,argv);// 创建 HTTP 服务器对象// 构造函数中会自动创建 QTcpServer 并监听 8080 端口myHttpServer server;// 进入 Qt 事件循环等待并处理各类事件如新的网络连接// 程序会一直运行直到收到退出信号returna.exec();}myhttpserver.h/** * file myhttpserver.h * brief myHttpServer 类的声明文件 * * 该类基于 Qt 的 QTcpServer / QTcpSocket 实现了一个极简的 HTTP Web 服务器 * - 监听本机所有网卡的 8080 端口 * - 当有浏览器客户端连接进来时返回一段简单的 HTML 页面 * - 页面内容包含一个每秒自增的访问计数 * * 本文件只负责类的声明具体实现见 myhttpserver.cpp。 */#ifndefMYHTTPSERVER_H#defineMYHTTPSERVER_H#includeQObject// 所有 Qt 对象的基类提供信号槽机制#includeQTcpServer// TCP 服务器类用于监听端口、接受客户端连接#includeQTcpSocket// TCP 套接字类用于与单个客户端进行数据收发/** * class myHttpServer * brief 一个极简的 HTTP Web 服务器类 * * 继承自 QObject以便使用 Qt 的信号与槽机制。 * 主要工作流程 * 1. 构造函数中创建 QTcpServer 并监听 8080 端口 * 2. 有新连接到来时触发 newConnection 信号 * 3. 在 connection() 槽函数中读取请求、返回 HTML 响应 */classmyHttpServer:publicQObject{Q_OBJECT// 使用信号槽等 Qt 元对象特性所必需的宏public:/** * brief 构造函数 * param parent 父对象指针默认为 nullptr * * 内部会创建 QTcpServer 并开始监听 8080 端口。 */explicitmyHttpServer(QObject*parentnullptr);/** * brief 析构函数 * 负责释放当前连接的 socket 等资源。 */~myHttpServer();public:/** * brief 当前连接的客户端套接字指针 * * 每次有新客户端连接时都会在 connection() 中被重新赋值 * 指向最新的那个客户端连接。 */QTcpSocket*socket;private:/** * brief TCP 服务器对象指针 * 负责监听端口并在有客户端连接时发出 newConnection 信号。 */QTcpServer*ser;/** * brief 获取当前套接字中可读的字节数 * return 可读字节数 * * 注意该函数当前只有声明、没有实现属于遗留/占位声明。 */qint64bytesAvailable()const;publicslots:/** * brief 处理新客户端连接的槽函数 * * 当 QTcpServer 发出 newConnection 信号时被自动调用。 * 负责读取浏览器发来的请求数据并向浏览器返回一段 HTML 页面。 */voidconnection();};#endif// MYHTTPSERVER_Hmyhttpserver.cpp/** * file myhttpserver.cpp * brief myHttpServer 类的实现文件 * * 实现了一个极简的 HTTP Web 服务器工作流程如下 * 1. 构造函数创建 QTcpServer监听 8080 端口 * 2. connection()处理新连接读取请求并返回 HTML 页面 */#includemyhttpserver.h#includeQDebug// 用于在控制台输出调试/运行信息/** * brief 构造函数 * param parent 父对象指针默认为 nullptr * * 初始化流程 * 1. 创建 QTcpServer 对象父对象为 thisQt 会自动管理其内存 * 2. 将 newConnection 信号连接到 connection() 槽函数 * 3. 调用 listen() 监听本机所有网卡QHostAddress::Any的 8080 端口 * 4. 根据监听是否成功在控制台输出相应的提示信息 */myHttpServer::myHttpServer(QObject*parent):QObject(parent){// 创建一个 TCP 服务器对象this 作为其父对象便于 Qt 自动释放内存sernewQTcpServer(this);// 信号与槽// 当有新的客户端连接到来时QTcpServer 会发出 newConnection 信号// 这里将其连接到本类的 connection() 槽函数以处理该连接connect(ser,QTcpServer::newConnection,this,myHttpServer::connection);// 尝试监听本机所有网卡地址的 8080 端口if(!ser-listen(QHostAddress::Any,8080)){// 监听失败输出错误信息提示用户检查端口占用或权限问题qDebug()致命错误web服务器没有启动请重新检查endl;}else{// 监听成功输出正常启动提示qDebug()正常启动web服务器端口8080,等待客户端连接....endl;}}/** * brief 处理新客户端连接的槽函数 * * 当 QTcpServer 发出 newConnection 信号时被调用执行以下操作 * 1. 取出待处理的新连接QTcpSocket * 2. 等待并读取浏览器发来的 HTTP 请求数据 * 3. 构造并返回一个简单的 HTTP 响应HTML 页面 * 4. 页面中显示一个每秒自增的访问计数 */voidmyHttpServer::connection(){// 取出下一个待处理的客户端连接赋值给成员变量 socket// 注意这里直接使用成员变量保存连接若同时有多个连接会互相覆盖socketser-nextPendingConnection();// 循环等待直到套接字上有可读数据每次最多等待 100 毫秒// waitForReadyRead(100) 返回 true 表示有数据可读返回 false 则继续等待while(!(socket-waitForReadyRead(100)));// 定义一个缓冲区用于存放从客户端读取到的请求数据charwebdata[1000];// 从套接字读取最多 1000 字节的请求数据到缓冲区// sv 为实际读取到的字节数当前未使用属于冗余变量intsvsocket-read(webdata,1000);// 在控制台打印读取到的请求信息便于调试观察qDebug()正常运行从浏览器读取数据信息QString(webdata)endl;// ---------- 下面开始构造 HTTP 响应头 ----------// 状态行HTTP/1.1 200 OK表示请求成功socket-write(HTTP/1.1 200 OK\r\n);// 响应头声明返回内容的类型为 HTML 文本socket-write(Content-Type:text/html\r\n);// 响应头声明连接在本次响应结束后关闭Connection: closesocket-write(Connection:close\r\n);// 响应头Refresh:1 表示浏览器每隔 1 秒自动刷新一次页面socket-write(Refresh:1\r\n\r\n);// 每秒刷新web浏览器// ---------- 下面开始构造 HTML 页面内容 ----------// 依次写入 HTML 文档结构// !DOCTYPE 文档类型声明// html HTML 根标签// header 头部区域// meta ... 设置字符集为 UTF-8避免中文乱码// title 浏览器标签页标题// body 页面主体显示连接提示信息socket-write(!DOCTYPEhtmlheadermeta http-equiv\content-Type\ content\text/html;charsetutf-8\/titlehttpserver/title/headerbody客户端已经连接到httpServer服务器秒数为/body);// 定义一个字节数组用于存放访问计数QByteArray bytesary;// 声明静态计数器静态变量只会初始化一次之后每次调用都会保留上一次的值// 用于统计服务器启动以来的访问次数并在页面上显示staticqint16 icount;// 将当前计数值写入字节数组同时 icount 自增 1先用后加bytesary.setNum(icount);// 把计数值写入响应追加到 HTML 中 body 之后socket-write(bytesary);// 写入 HTML 结束标签socket-write(/html);// 立即将缓冲区中的数据发送给客户端socket-flush();// 当客户端断开连接时让该 socket 自动删除自身释放内存connect(socket,QTcpSocket::disconnected,socket,QTcpSocket::deleteLater);// 主动断开与客户端的连接配合上面的 Connection: close 响应头socket-disconnectFromHost();}/** * brief 析构函数 * * 关闭当前连接的套接字释放连接资源。 */myHttpServer::~myHttpServer(){socket-close();}关键点小结HTTP 报文本质响应头 空行 响应体代码里用多个socket-write()拼接而成其中\r\n是换行符\r\n\r\n表示「头部结束、正文开始」。Refresh: 1让浏览器每隔 1 秒自动重新请求一次页面从而实现「计数每秒自增」的效果。Connection: close告知浏览器本次响应结束后关闭连接服务端随后调用disconnectFromHost()主动断开。static qint16 icount静态局部变量只初始化一次之后每次进入函数都保留上次的值用于累计访问次数。