目录一、从自定义协议到 HTTP1. 什么是 HTTP2. 为什么需要 HTTP二、URL1. URL 结构2. URL 编码机制三、HTTP 请求1. 请求报文2. 请求行3. 请求报头4. 空行5. 请求正文四、HTTP 响应1. 响应报文2. 状态行3. 响应报头4. 空行5. 响应正文五、HTTP方法1. GET获取资源2. POST传输实体/创建资源3. PUT更新/替换资源4. DELETE删除资源5. 其他 HTTP 方法六、HTTP 状态码1. 1xx2. 2xx3. 3xx4. 4xx5. 5xx七、常见报头与 Connection 连接管理1. 常见 HTTP 报头总览2. Connection 报头与网络连接管理八、HTTP 发展史各版本特征与技术演进总结一、从自定义协议到 HTTP在上一篇博客中我们针对网络计算器项目设计并实现了一套自定义应用层协议。通过该协议我们成功解决了 TCP 字节流传输中的粘包与半包问题实现了结构化数据在网络两端的解包与还原这种点对点定制的自定义协议在业务单一、封闭的内部系统中非常高效。然而如果面对的是全球互联、业务错综复杂的互联网环境为每个应用都设计一套私有协议显然是不切实际的。为了解决通用通用性与标准化问题现代互联网架构选择广泛采用HTTP 协议1. 什么是 HTTP在展开 HTTP 之前我们需要明确为什么自定义协议无法直接承载现代互联网的宏大生态。在网络计算器的实现中我们定义了专属的 Request 和 Response 结构体并规定了具体的边界序列化格式。这种设计存在以下两点局限缺乏扩展性我们的协议只能传输左操作数、右操作数和运算符。如果明天业务变更需要支持文件下载、或用户登录验证原有的解析逻辑和结构体字段就必须全部推倒重写缺乏互操作性由于协议是私有的只有我们自己编写的客户端才能与该服务端通信。外界的浏览器、APP 无法识别我们定义的报文格式导致该服务成为一个无法融入公共互联网生态的孤岛因此互联网需要一套通用的、扩展性极强的应用层协议这便是 HTTP 诞生的历史必然什么是 HTTP 协议HTTPHyperText Transfer Protocol超文本传输协议是分布式、协作式和超媒体信息系统的应用层协议。它最初由蒂姆·伯纳斯-李等人于 1989 年发起旨在实现全球学术资源的共享与互联HTTP 具有以下核心特征应用层协议HTTP 位于 TCP/IP 模型的应用层。它本身不关心底层的寻址与传输而是默认信任下层通常是 TCP 协议已经建立好了可靠的全双工字节流通道请求-响应模型HTTP 协议的通信永远由客户端通常是浏览器或移动端 App发起请求服务端Web 服务器接收并处理后再返回响应。服务端不会主动向客户端推送数据注此处特指传统的标准 HTTP 交互模式无状态性在协议定义层面HTTP 服务器不会主动记录多次请求之间的关联。对于服务器而言每一次收到的 HTTP 请求都是完全独立的。这种设计极大地降低了服务器的架构复杂度有利于服务端的横向扩展2. 为什么需要 HTTP在明确了什么是 HTTP 之后我们还需要理解其在互联网工程中的不可替代性① 统一的标准契约得益于 HTTP 协议的公开与标准化全球的软件厂商才能够在同一套语境下协作。各大浏览器厂商只需专注于如何解析并渲染服务器返回的超文本数据而服务器厂商和后端开发者则只需按标准格式组织并传输数据即可② 语义丰富与多媒体承载能力早期的 HTTP 主要用于传输纯文本的 HTML 网页。但由于其优秀的设计现代 HTTP 已经可以承载任何形式的数据媒体包括但不限于静态资源图片PNG/JPEG、音频MP3、视频MP4、字体文件、CSS、JavaScript结构化动态数据JSON、XML③ 网络治理控制HTTP 不仅仅规定了数据的传输格式其报文内部还内置了一套极其完善的系统控制字段。通过这些标准化字段协议直接在应用层原生支持了以下工业级功能缓存控制通过 Header 字段控制浏览器是否直接使用本地缓存大幅减轻后端服务器的带宽压力内容协商客户端可以声明自己支持的语言、字符集和压缩算法服务端据此动态返回最匹配的资源格式身份认证与状态维持虽然 HTTP 本身无状态但通过引入 Cookie、Session 以及现代的 Token 机制可以在 HTTP 协议之上构建出复杂的有状态业务二、URLURL是互联网上标准资源的地址。互联网上的每一个文件如 HTML 网页、图片、音视频、动态数据接口都拥有一个唯一的 URL在日常生活中人们常说的 网址 通常是指指向某个网站主页的简化 URL例如 www.example.com。而在工程实践中完整的 URL 包含了定位服务器、找到特定资源文件以及传递参数所需的全部上下文信息需要补充的是URL 属于URI的一个子集。URI 负责唯一标识一个资源而 URL 不仅标识了资源还指明了如何通过网络协议去定位和访问该资源1. URL 结构为了清晰地理解资源是如何被定位的我们用一个典型的网址为例进行拆解分析一个完整的 URL 通常由以下几个核心部分按顺序级联而成。它们的职责边界如下组成部分实例概念与职责协议方案名https声明客户端与服务端通信时采用的应用层协议类型。常见的有 http、https、ftp 等。它决定了浏览器后续将采用何种解析机制域名/主机名www.example.com标识目标资源所在的 Web 服务器。在底层网络传输时浏览器会先通过 DNS 将该域名解析为对应的目标 IP 地址。此处也可以直接填写服务器的物理 IP端口号8080标识目标服务器上接收网络连接的特定进程。注意如果采用标准协议且使用默认端口则端口号在 URL 中通常可以省略浏览器会自动补齐资源路径/path/index.html标识资源在服务器文件系统中的相对路径。它决定了请求落地到服务器的哪一个具体的 HTML 文件或后端路由控制器上查询字符串id1客户端传递给服务器的动态参数。它以 ? 作为起始标记内部通常由一个或多个 keyvalue 的键值对组成多个键值对之间使用 符号进行分隔例如 ?id1namejack补充细节片段标识符虽然给出的示例中未包含但完整的 URL 规范中还支持在末尾通过 # 引入片段标识符如 /index.html#section1。它通常用于标识网页内部的某个特定锚点位置浏览器在加载完页面后会自动滚动到该位置。注意片段标识符属于纯客户端行为在发起 HTTP 请求时不会被发送给服务器2. URL 编码机制在网络传输中URL 必须具备极高的确定性但这也带来了字符集冲突的工程问题① 为什么需要 URL 编码URL 的设计规范规定URL 中只能包含英文字母、数字以及少数几个特殊的安全特殊字符然而以下两类场景会破坏 URL 的正常解析控制字符冲突URL 本身利用 ?、、、/、: 等特殊符号来切分结构。如果用户的查询字符串内容里也包含了这些字符如果不做处理后端的 URL 解析器就会将其误判为参数分隔符导致语义崩溃非 ASCII 字符如果直接在路径或参数中传入中文由于不同操作系统的终端或浏览器采用的字符集标准如 UTF-8、GBK不同直接传输裸字节流极易引发服务端的乱码故障为了解决这个问题URL 采用了一套标准的编码机制在工业界被称为URL 编码或百分号编码② 编码的底层转换机制将不支持的特殊字符或多字节中文字符按照某种既定的字符集现代互联网默认采用UTF-8转为原始的二进制字节流然后将每个字节转换为两位十六进制数并在其前面加上百分号 % 进行显式标记空格字符在 ASCII 中为 32十六进制为 0x20经过 URL 编码后表现为 %20在某些查询字符串规范中也会被编码为 中文字符 在 UTF-8 编码下网 字独占 3 个字节其底层的十六进制数据为 E7 BD 91。 经过 URL 百分号编码转换后它在网址中最终呈现的形态为%E7%BD%91通过这种机制任何复杂的文本内容在网络层流转时都会被收敛为 ASCII 码可见字符从而保证了应用层协议在解析控制面上的绝对安全三、HTTP 请求一个完整的 HTTP 请求报文在物理链路流转时由四个核心部分按序级联而成请求行、请求报头、空行以及请求正文1. 请求报文在协议定义层面HTTP 请求报文的宏观拓扑结构如下所示2. 请求行请求行位于 HTTP 请求的第一行用于明确指出本次网络交互的核心意图与协议边界。它由三个基本字段级联而成字段之间使用标准的空格进行分隔行尾以 \r\n 结束① 请求格式② 字段解析方法用以告知服务器应当对目标资源执行何种操作。最常见的有 GET获取资源和 POST传输实体正文URL/URI资源路径与参数控制面。需要注意的是当客户端直接向源服务器发起请求时此处通常不需要填写完整的域名而只需填写从根目录开始的绝对路径与查询字符串如 /path/index.html?id1版本声明客户端当前所采用的 HTTP 协议版本。服务端会根据该版本号决定如何进行响应3. 请求报头请求报头自请求行的下一行开始可以包含任意多行直到遭遇空行为止。它由一系列的键值对组成用以向服务端传递本次请求的元数据① 报头格式每行包含一个属性项严格遵循如下语法规范属性名: 属性值\r\n注意冒号:后面必须紧跟一个空格这是 HTTP RFC 标准规定的语法标记② 报头的职责请求报头相当于请求报文的 配置控制面板。通过这些键值对客户端可以声明目标主机的域名Host客户端自身的操作系统与浏览器内核信息User-Agent期望接收的数据类型Accept本次请求正文的长度Content-Length等4. 空行在请求报头结束之后必须存在一个只包含 \r\n 的独立行这便是空行为什么需要空行在前面的自定义协议博客中我们曾深入探讨过 TCP 字节流的粘包与半包问题。HTTP 协议作为应用层标准同样必须在线性流动的字节序列中精准截断当服务端的网络线程从底层的内核接收缓冲区中读取 HTTP 报文时由于报头的行数是不固定的客户端可以随意携带自定义 Header解析器无法预知何时结束空行的出现提供了一个明确的状态机转换信号只要解析器持续读取到 Key: Value\r\n 结构的文本就继续解析报头一旦在一行的开头直接遭遇 \r\n即该行没有任何字符就直接换行解析器便能立即判定——报头的元数据结束后续的所有字节都将归属于请求正文。5. 请求正文请求正文是 HTTP 请求承载的核心业务负载。它不属于文本流的强制控制行而是根据业务按需存在① 正文特征多媒体承载力正文内部不受 Key: Value\r\n 文本格式的约束。它可以是纯 JSON 字符串、表单键值对、甚至是上传的图片或文件的二进制裸数据存在性规律通常情况下GET、DELETE 请求是不包含请求正文的它们的参数直接挂载在 URL 的查询字符串中而 POST、PUT 等涉及向服务器提交、修改数据的请求其核心数据均存放在正文中② 正文与 Content-Length 的双层防御性配由于请求正文内部可能包含任何不可见的二进制数据为了防止解析器在读取正文时发生边界误判HTTP 协议建立了严密的控制闭环核心机制如果一个 HTTP 请求包含正文其请求报头中必须携带 Content-Length 字段显式声明正文的字节数服务端的解析器在探测到空行后会立即提取之前解析出的 Content-Length 数值 N随后由底层的读取例程在套接字流中连续向后读取 N 个字节。将正文完整切出四、HTTP 响应当 HTTP 请求报文经由网络到达服务端并被后端的网络驱动引擎与业务逻辑层处理完毕后服务端会生成一份对应的HTTP 响应报文并返回给客户端与请求报文类似HTTP 响应报文同样是一种高度规范化的文本协议。响应报文同样由四个核心部分组成状态行、响应报头、空行以及响应正文1. 响应报文HTTP 响应报文的宏观拓扑结构如下所示2. 状态行状态行位于 HTTP 响应报文的第一行用于向客户端宣告本次请求的处理状态。它由三个基本字段组成字段之间使用标准的空格进行分隔行尾以 \r\n 结束① 请求格式② 字段解析版本声明服务端用来支撑本次会话的 HTTP 协议版本如 HTTP/1.1。通常情况下服务端会尽可能与客户端请求中携带的版本号保持一致状态码一个三位数的整数用以在代码层面精确标识请求的处理结果状态码描述紧跟在状态码后面的文本短语用以对状态码进行可读性的解释如 OK、Not Found。主要用于调试和日志回显3. 响应报头响应报头用以传递关于服务器自身以及响应正文的元数据。其语法规范与请求报头完全一致由多行 Key: Value\r\n 结构组成典型的响应报头项通过响应报头服务端可以向客户端声明编写、托管该 Web 服务的软件名称与版本Server响应生成的物理时间Date响应正文的具体媒体类型引导浏览器如何渲染Content-Typecharset响应正文的精确字节长度Content-Length4. 空行响应报头结束之后必须附带一个只包含 \r\n 的空行边界切分作用在服务端回显的高并发流转中客户端同样需要面临底层 TCP 字节流的粘包与半包挑战。客户端在持续读取物理字符时一旦探测到独立的 \r\n 换行便意味着状态行和所有响应报头已接收完毕。此时解析器会立即切换状态准备依据报头中的长度指引去提取后续的响应正文5. 响应正文响应正文是服务端交付给客户端的实质性资源也是 HTTP 报文中体量最大的部分① 正文的物理承载响应正文的内容完全由业务场景决定。在传统的 Web 浏览中它通常是完整的 HTML 源代码、CSS 样式表或 JavaScript 脚本在微服务架构中它通常是经过序列化后的 JSON 字符串在资源下载场景中它则是图片、音频或视频等原始二进制流② 解析终止机制与请求正文相同响应正文的长度同样由响应报头中的 Content-Length 字段进行约束。在某些无法预知总长度的场景中服务端也会采用分块传输编码。此时响应正文将被切分为多个带有长度声明的独立数据块直到遭遇一个长度为 0 的块客户端便可判定该响应报文正式闭环五、HTTP方法在 HTTP 协议的官方规范中共定义了十余种请求方法用以向服务器表明所期望的执行操作。在实际开发与网络交互中绝大多数场景仅由其中少数几种核心方法承载本节将重点对最常用的四种核心方法进行解构而将其余的方法通过表格形式进行归纳在进入具体方法之前需要明确两个评估 HTTP 方法行为的关键指标安全性指该方法的执行是否会改变服务器上的资源状态。不会引发服务器端数据状态变更的方法被称为 安全的幂等性指连续执行多次相同的请求其对服务器资源产生的副作用是否相同。如果执行一次和执行多次的结果完全一致则该方法是 幂等的1. GET获取资源GET 是互联网中频率最高的方法主要用于请求获取指定的资源语义定义向服务器索取特定 URI 对应的资源实体行为特征安全性是。GET 理论上只用于读取数据不应修改服务器上的任何状态幂等性是。由于不改变状态连续发起 10 次相同的 GET 请求服务器上的数据依然保持原样返回的资源内容也应当是相同的数据承载GET 请求通常不包含请求正文。如果需要传递参数必须将参数以键值对的形式挂载在 URL 的查询字符串中2. POST传输实体/创建资源POST 方法常用于向服务器提交数据通常会导致服务器端状态的改变或产生副作用语义定义向指定的资源提交实体交由服务器端对应的程序进行处理例如提交注册表单、发布评论等行为特征安全性否。执行 POST 请求通常会在服务器端写入或创建新数据因此会改变服务器状态幂等性否。如果连续点击 3 次提交订单的 POST 按钮在没有后端防重策略的情况下服务器可能会创建 3 笔不同的订单从而产生不同的副作用数据承载参数和核心业务负载通常存放在请求正文中能够承载体量较大或结构复杂的结构化数据3. PUT更新/替换资源PUT 方法主要用于向服务器上传或替换指定的资源语义定义将请求正文中的实体放置在指定的 URI 位置。如果该 URI 对应的资源已经存在则用新实体将其覆盖如果资源不存在则在对应的 URI 处创建该资源行为特征安全性否。因为涉及数据的写入和覆盖会改变服务器的状态。幂等性是。由于 PUT 的语义是 资源全量替换因此无论连续执行多少次相同的 PUT操作该 URI 对应的资源都只会保持为最新状态数据承载被替换的完整资源实体放置在请求正文中URI 则指向被替换的目标4. DELETE删除资源DELETE 方法的语义非常直观用于请求服务器删除指定的资源语义定义请求服务器删除 URI 识别出的指定资源行为特征安全性否。删除操作会移除服务器上的现有数据改变系统状态幂等性是。第一次执行 DELETE /user/1 时成功将用户 1 删除后续再次执行相同的请求时虽然服务器可能会返回 资源不存在 的错误提示但服务器上的资源状态并不会发生进一步的变化5. 其他 HTTP 方法除了上述四种核心方法外HTTP 协议还定义了其他一些用于网络协同控制、链路诊断和元数据探测的方法。下表列出了这些方法的具体语义及其工程行为特征方法名称核心职责安全性幂等性HEAD与 GET 完全一致但服务器响应时只返回状态行和响应报头绝对不返回响应正文。常用于在不下载文件的情况下探测文件的长度或是否被修改是是OPTIONS用于查询特定 URI 资源所支持的通信选项或方法。在前端跨域请求中浏览器常先发送 OPTIONS 进行预检请求是是PATCH用于对已知资源进行局部/部分更新与 PUT 的全量替换相对。由于只更新部分字段连续执行可能引发不同的业务递增效果故不保证幂等否否CONNECT保留方法用以要求服务器与目标站点建立一个隧道。通常用于在代理服务器场景下建立安全的 SSL/TLS 隧道通信否否TRACE用于激发一个远程的应用层环回测试。服务器收到请求后会将其原封不动地返回以便客户端观察请求在传输链路上是否被代理服务器篡改是是六、HTTP 状态码HTTP 状态码是表示服务器对请求的处理结果的数字代码。旨在让客户端的协议解析器能够快速审判业务状态进而执行对应的控制状态码的第一个数字定义了响应的类别后两位数字没有分类作用。根据 HTTP 标准状态码共分为五大类状态码类别核心语义1xx信息性状态码请求已被服务器接收需要继续处理或切换协议2xx成功状态码请求已被服务器成功接收、理解并接受处理3xx重定向状态码资源位置已发生变更客户端需要执行进一步的操作以完成请求4xx客户端错误状态码客户端发送的请求存在语法错误或无法被服务器识别、执行5xx服务端错误状态码服务器在处理请求的过程中内部发生故障或无法满足合法请求1. 1xx1xx 系列状态码属于提示信息表示请求已被接收正在等待后续处理。这类响应在日常普通的 HTTP 通信中较少出现101 Switching Protocols切换协议 客户端请求服务器切换到更高级的协议。在现代 Web 工程中它是WebSocket握手阶段的核心状态码。当浏览器试图与后端建立全双工的长连接时服务器若同意升级协议便会返回 101随后底层的 TCP 通道将由 WebSocket 协议驱动2. 2xx2xx 系列状态码表明客户端发起的网络意图在服务端已顺利完成200 OK成功 最常见的目标状态。表示请求在服务端已被正确处理且期望的响应资源将随响应正文一同返回给客户端204 No Content无内容 请求处理成功但响应报文中不包含任何响应正文。常用于一些只需要向客户端返回执行结果、而不需要刷新的业务场景206 Partial Content部分内容 表示服务器已经成功处理了部分 GET 请求。这是断点续传与多线程并发下载的底层基石。客户端在请求时通过 Range 报头声明所需的字节范围服务器处理后返回 206并在 Body 中仅填充该片段的数据3. 3xx3xx 系列状态码表明资源的位置发生变动客户端必须根据响应报头中的字段发起二次请求301 Moved Permanently永久重定向 请求的资源已被分配了新的永久 URI。浏览器在收到该状态码后未来再次访问原网址时会自动跳转到新的地址302 Found临时重定向 请求的资源临时被移动到了新的 URI。由于移动是暂时的浏览器不会缓存该跳转关系后续的请求依然会先访问原网址304 Not Modified未修改 这是一个非常特殊的重定向状态。它并不代表资源发生了物理位移。客户端在发起请求时携带了本地缓存的标记服务器比对后发现资源未发生任何变化便返回 304 且不携带 Body。此时浏览器直接读取本地磁盘中的缓存极大地节省带宽4. 4xx4xx 系列状态码表示错误因客户端而起。通常是由于请求报文格式不合法、参数缺陷或权限不足导致的400 Bad Request错误请求 这是一个通用的客户端语义错误状态。表明客户端发送的请求报文存在明显的语法错误导致后端的路由或解析器拒绝继续向下流转401 Unauthorized未认证 表明当前请求需要进行用户身份验证。当客户端未携带合法的 Token、Session或认证信息已过期时服务端会发出此状态码以提示客户端先完成登录403 Forbidden禁止访问 服务器已经理解了请求但是拒绝执行。这通常与身份验证无关而是由于权限隔离导致的404 Not Found未找到 表明服务器上无法找到请求的 URI 对应的资源。在现代 Web 开发中它也经常被后端开发者用作一种防御性返回用以隐蔽由于权限不足而不愿向特定客户端公开的敏感接口5. 5xx5xx 系列状态码意味着请求报文在格式上没有任何缺陷但服务器在尝试执行该请求时其内部系统发生了逻辑崩溃或资源耗尽500 Internal Server Error服务器内部错误 通用的服务端崩溃状态。在实际工程中通常意味着后端的业务代码抛出了未经捕获的异常502 Bad Gateway错误网关 作为网关或代理服务器在执行路由转发时从上游的真实业务服务器收到了一个格式非法的响应。这通常说明后端微服务集群的真实节点处于崩溃或未启动状态503 Service Unavailable服务不可用 表明服务器当前因超载或停机维护而无法处理请求。它具有临时性的语义特征常伴随 Retry-After 报头一同返回告知客户端在特定时间后再进行重试七、常见报头与 Connection 连接管理在 HTTP 协议中报头承担着传输层之上的控制面职责。无论是请求报头还是响应报头它们都通过标准的 KV 键值对向对端传递关于报文主体、客户端环境等元数据1. 常见 HTTP 报头总览根据 RFC 规范报头根据其出现的语境可分为通用报头、请求报头、响应报头和实体报头。以下是线上生产环境中最频繁流转的键值对报头名语境示例语义与职责Host请求www.example.com:8080唯一强制报头。指明请求的目标域名和端口号User-Agent请求Mozilla/5.0... Chrome/120.0声明发起请求的客户端操作系统、浏览器内核及版本Content-Type通用application/json; charsetutf-8声明发送的实体正文类型Content-Length通用1024声明实体正文的绝对字节长度Accept请求text/html, application/json客户端声明自己能够理解并接收的类型Location响应https://new.example.com/login伴随 3xx 状态码出现向浏览器明示资源转移后的新绝对路径驱动其发起二次请求Server响应Nginx/1.24.0声明服务器软件名称及版本号。在线上生产环境中常将其隐藏以防止黑客针对特定版本漏洞实施攻击2. Connection 报头与网络连接管理① HTTP/1.0 的短连接模式Connection: close在早期的 HTTP/1.0 协议中默认采用的是短连接模型运作机制客户端与服务端建立 TCP 连接 - 发送 HTTP 请求 - 服务端处理并返回 HTTP 响应 -服务端立即调用 close() 断开 TCP 连接工程缺陷这意味着每一次 HTTP 请求-响应的交互都需要完整经历 TCP 的三次握手与四次挥手。在动辄包含上百个静态资源的网页中这种高频创建和销毁套接字的开销会带来严重的网络延迟极大地消耗操作系统的文件描述符与内存资源② HTTP/1.1 的长连接机制Connection: keep-alive为了扭转短连接在并发场景下的劣势HTTP/1.1 引入了持久连接机制并在工业界通称为长连接运作机制客户端在请求报头中携带 Connection: keep-alive。服务端收到后若同意保持连接则在响应报头中同样回显 Connection: keep-alive。在完成一次请求-响应之后底层的 TCP 套接字通道不会被销毁而是继续保持建立状态复用链路客户端可以利用这条现成的通道紧接着发送后续的多个 HTTP 请求③ 持久连接的工业控制面长连接虽然大幅降低了链路创建的开销但也带来了新的资源管理问题如果客户端持续占用通道而不发送数据服务器的并发承载力很快就会被枯竭的套接字资源锁死。为此工业界设计了以下双层防御机制超时断开服务器通常配置有保持存活的时间阈值。如果一条连接在连续一定时间内没有产生任何新的 HTTP 报文服务端将强制回收该套接字请求次数限制限制一条长连接上允许传输的最大请求轮数。当某个 TCP 通道上累计完成了 一定次数请求交互后即使未超时服务端也会在最后一次响应中将报头改写为 Connection: close随后关闭连接强制客户端下一次重新建连以此实现负载均衡与资源净化的目的八、HTTP 发展史HTTP 协议自诞生以来为了适应互联网海量数据和高并发业务的需求经历了几次重大的版本迭代。理解这一演进历史有助于我们更深刻地理解现代网络优化技术的演进方向各版本特征与技术演进① HTTP/0.91991年纯文本原型阶段特征极其简单只支持 GET 一种请求方法局限性没有请求头和响应头的概念服务端只能返回纯文本的 HTML 字符串无法传输图片、音频等其他媒体文件② HTTP/1.01996年多媒体支撑特征正式引入了请求头、响应头、状态码以及 POST 等方法进步支持了 Content-Type使得传输图片、压缩包、音视频等多媒体数据成为现实缺陷如前文所述默认采用短连接模式每次请求都需要建立 TCP 连接性能开销极大③ HTTP/1.11997年现代万维网的基石特征这是目前互联网中应用最广泛、最成熟的版本核心改进持久连接默认开启 Connection: keep-alive支持在单条 TCP 连接上连续发送多个请求虚拟主机支持引入了 Host 报头允许一台物理服务器托管多个不同域名的网站缓存控制引入了更完善的缓存控制机制局限性存在队头阻塞问题。在一条 TCP 连接中请求是串行处理的如果前面的请求处理较慢会阻塞后续所有请求的响应④ HTTP/22015年多路复用优化特征不改变 HTTP 的语义方法、状态码、报头依然不变全面重构了底层的传输编码核心改进二进制分帧将原本的纯文本报文改写为底层的二进制帧Frame解析效率更高多路复用允许在单条 TCP 连接上并发交错地发送任意数量的请求与响应彻底解决了 HTTP 层的队头阻塞问题头部压缩引入 HPACK 算法压缩报头避免了重复报头产生的带宽浪费⑤ HTTP/32022年正式标准化基于 UDP 的彻底重构特征为了解决 TCP 协议固有的局限性HTTP/3 将底层的传输层协议由 TCP 直接替换为基于 UDP 的QUIC 协议核心改进解决传输层队头阻塞在 HTTP/2 中如果底层的 TCP 发生丢包整个连接都会陷入停顿。QUIC 协议在 UDP 之上实现了独立的流控制单个流丢包不会影响其他流极速建连将传输层握手与加密层握手合二为一实现了 1-RTT 甚至 0-RTT 的连接建立连接迁移由于不使用 IP 端口 作为连接标识当移动设备在 Wi-Fi 和蜂窝网络之间切换时网络连接不会发生中断提供了更平滑的用户体验总结综上所述从 URL、HTTP 请求与响应报文到请求方法、状态码以及各种常见 Header我们已经对 HTTP 协议建立了一个较为完整的整体认知回顾前面学习过的自定义协议可以发现HTTP 本质上仍然是一种应用层协议。只不过相比我们自己设计的网络计算器协议HTTP 经过了数十年的发展与演化已经形成了一套成熟、统一且被全世界广泛接受的通信规范与此同时通过对持久连接以及 HTTP 各版本演进过程的了解我们也逐渐认识到HTTP解决的是如何组织和描述数据TCP解决的是如何可靠地传输数据二者相互协作最终构成了现代互联网最重要的通信基础设施之一至此我们已经具备了阅读和解析 HTTP 报文所需的理论基础。在下一篇中我们将正式进入 HTTP 编程实践阶段尝试基于前面实现的 TCP 服务器框架编写属于自己的 HTTP Server并亲手完成对 HTTP 请求与响应报文的解析与构造