当前位置: 首页 > news >正文

QT_HTTP协议编程

QT_HTTP协议编程

http超文本传输协议,是一个简单的请求响应协议,它通常运行在TCP之上,作用规定WWW服务器与浏览器之间信息传递规范,是二者共同遵守的协议。
http工作原理:http是基于客户/服务器模式,且面向连接的,http事务处理流程

  1. 客户与服务器建立连接
  2. 客户想服务器提出请求
  3. 服务器接受请求,并根据请求返回相应的文件作为应答
  4. 客户与服务器关闭连接

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-alive

2. 响应报文(Response)

状态行(版本 + 状态码 + 原因短语) 响应头 空行 响应体(服务器返回的实际内容)

示例(对应项目二的服务器返回):

HTTP/1.1 200 OK Content-Type: text/html Connection: close Refresh: 1 <!DOCTYPE><html>...</html>

3. 常用请求方法(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/htmlapplication/json
  • Content-Length:正文的字节数。
  • Connectionkeep-alive(保持连接)或close(关闭连接)。
  • Refresh:让浏览器在指定秒数后自动刷新页面(项目二中用到)。

相关API

Qt 中进行 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 += network,Qt 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 设计器中拖入以下控件即可(对应代码中的对象名):

  • 一个QPushButton,objectName 改为getDataBtn,文字如「获取数据」。
  • 一个QLabel,objectName 为label,用于显示下载状态。
  • 一个QTextBrowser(或QPlainTextEdit),objectName 为textBrowser,用于显示返回内容。

代码文件

mainwindow.h

#ifndefMAINWINDOW_H#defineMAINWINDOW_H#include<QMainWindow>#include<QtNetwork>// 提供编程Tcp/IP客户端和服务器的类#include<QUrl>// 提供接口使用URLsclassQNetworkAccessManager;classQNetworkReply;// 此类是QIODevice的子类(补充:原注释 “QIDevice” 应为 QIODevice)QT_BEGIN_NAMESPACEnamespaceUi{classMainWindow;}QT_END_NAMESPACEclassMainWindow:publicQMainWindow{Q_OBJECTpublic:MainWindow(QWidget*parent=nullptr);~MainWindow();private:Ui::MainWindow*ui;QNetworkAccessManager*mgr;publicslots:voidreplayFinishedFunc(QNetworkReply*);//响应(补充:函数名语义应为 replyFinishedFunc,与信号 finished 对应)privateslots:voidon_getDataBtn_clicked();};#endif// MAINWINDOW_H

mainwindow.cpp

#include"mainwindow.h"#include"ui_mainwindow.h"MainWindow::MainWindow(QWidget*parent):QMainWindow(parent),ui(newUi::MainWindow){ui->setupUi(this);mgr=newQNetworkAccessManager(this);connect(mgr,SIGNAL(finished(QNetworkReply*)),this,SLOT(replayFinishedFunc(QNetworkReply*)));}MainWindow::~MainWindow(){deleteui;}voidMainWindow::replayFinishedFunc(QNetworkReply*reply){QString strall=reply->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")));}

运行说明(补充)

  1. 点击「获取数据」按钮,程序会异步向http://www.baidu.com发起 GET 请求。
  2. 请求发出后,label显示「资源正在下载中,请耐心等待…」。
  3. 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

运行与测试方式(补充)

  1. 编译运行程序,控制台输出「正常启动:web服务器端口:8080」。
  2. 打开任意浏览器,访问http://localhost:8080(或本机 IP 加端口)。
  3. 页面显示「客户端已经连接到httpServer服务器,秒数为:N」,且因响应头含Refresh: 1,页面每秒自动刷新、数字每秒递增。

代码文件

main.cpp

/** * @file main.cpp * @brief 程序入口文件 * * 这是一个基于 Qt 控制台(无 GUI)的极简 HTTP Web 服务器示例程序。 * 程序启动后会创建 myHttpServer 对象,监听 8080 端口, * 当浏览器访问 http://localhost:8080 时,返回一段显示访问计数的 HTML 页面。 */#include<QCoreApplication>// 控制台应用程序类(不需要 GUI)#include"myhttpserver.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#include<QObject>// 所有 Qt 对象的基类,提供信号槽机制#include<QTcpServer>// TCP 服务器类,用于监听端口、接受客户端连接#include<QTcpSocket>// 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*parent=nullptr);/** * @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_H

myhttpserver.cpp

/** * @file myhttpserver.cpp * @brief myHttpServer 类的实现文件 * * 实现了一个极简的 HTTP Web 服务器,工作流程如下: * 1. 构造函数:创建 QTcpServer,监听 8080 端口 * 2. connection():处理新连接,读取请求并返回 HTML 页面 */#include"myhttpserver.h"#include<QDebug>// 用于在控制台输出调试/运行信息/** * @brief 构造函数 * @param parent 父对象指针,默认为 nullptr * * 初始化流程: * 1. 创建 QTcpServer 对象(父对象为 this,Qt 会自动管理其内存) * 2. 将 newConnection 信号连接到 connection() 槽函数 * 3. 调用 listen() 监听本机所有网卡(QHostAddress::Any)的 8080 端口 * 4. 根据监听是否成功,在控制台输出相应的提示信息 */myHttpServer::myHttpServer(QObject*parent):QObject(parent){// 创建一个 TCP 服务器对象,this 作为其父对象(便于 Qt 自动释放内存)ser=newQTcpServer(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// 注意:这里直接使用成员变量保存连接,若同时有多个连接会互相覆盖socket=ser->nextPendingConnection();// 循环等待,直到套接字上有可读数据(每次最多等待 100 毫秒)// waitForReadyRead(100) 返回 true 表示有数据可读,返回 false 则继续等待while(!(socket->waitForReadyRead(100)));// 定义一个缓冲区,用于存放从客户端读取到的请求数据charwebdata[1000];// 从套接字读取最多 1000 字节的请求数据到缓冲区// sv 为实际读取到的字节数(当前未使用,属于冗余变量)intsv=socket->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: close)socket->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("<!DOCTYPE>""<html>""<header>""<meta http-equiv=\"content-Type\" content=\"text/html;charset=utf-8\"/>""<title>httpserver</title>""</header>""<body>客户端已经连接到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:静态局部变量只初始化一次,之后每次进入函数都保留上次的值,用于累计访问次数。
http://www.cnnetsun.cn/news/4278770.html

相关文章:

  • 第 9 篇 OCC OCAF 框架详解:特征树、装配管理、数据持久化、参数化架构
  • AI技能市场化的关键:从提示词操作到稳定交付
  • 8万字BAT面经的高效使用指南:从题海到Offer收割
  • 蓝桥杯算法竞赛备赛全攻略:从省一到国二的实战心法与技巧
  • 两个字段都建了单列索引,为什么加了 OR,执行计划还是全表扫描?
  • Agent Skills 入门到实战:从 Prompt 到可复用技能封装
  • 毕业论文降 AI 什么时候该花钱?快降重 VS 笔灵 AI,教育学硕士知网 AIGC 实测避坑
  • AI时代开发者进阶指南:从Prompt到大模型工程实践
  • GraphRAG实战:基于代码知识图谱的代码库问答实现
  • 硬盘健康监控与故障预警:用Hard Disk Sentinel看懂SMART数据
  • AI应用盈利难?从算力成本到工程优化的实战指南
  • 基于Mahout协同过滤的电影推荐系统:Java工程实践与毕业设计指南
  • WordPress浏览量计数器插件:精准统计、缓存兼容与性能优化全攻略
  • Python学习路线全解析:爬虫、数据分析、AI与自动化办公实战指南
  • 英伟达70%营收预期下,AI算力规划与GPU部署实战指南
  • 2026年Java零基础暑期学习路线:从JDK安装到项目实战全攻略
  • 层次分析法实战指南:从多准则决策到结构化选择
  • 【已解决】docker desktop安装求助!!
  • 元初混沌体系 第三卷 卫星互联网全域周天拓扑体系:第五十四篇 中轨周天骨干层全场景拓扑闭环总结
  • 可解释AI与局部蒸馏:用随机森林与线性回归实战详解
  • MySQL 表的操作实战指南:创建、修改与删除
  • 单片机毕业设计-基于 STM32 单片机的车载温碳监测与智能通风控制系统设计 基于 STM32 的车内人员检测与环境智能调控装置设计(013605)
  • 基于LLM的双维度题目附带内容相似度分析框架解析
  • 拼多多 OCPX 稳定成本推广:一阶段、二阶段深度解析
  • 海鲜池开缸、巡检、换水与应急处理:一套可量化的日常操作规程
  • ASP.NET WebForms三层架构实战:从虚拟主机销售系统源码看经典B/S应用开发
  • 网易有道2018校招算法工程师笔试复盘:考点、编程题与备考策略
  • Kafka架构原理与面试实战:从高性能到可靠性全解析
  • Linux进程管理全面解析
  • Windows系统清理与提速:从底层原理到命令行实战指南