应用层 - HTTP¶
HTTP 是什么¶
HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等 超文本数据 的约定和规范。
HTML 是超文本的载体,使用各种标签描述文字、图片、超链接等资源
HTTP结构介绍¶
HTTP 报文结构¶
由 3 大部分组成:
- 起始行 (start line):描述请求或相应的基本信息
- 头部字段 (header):使用 key - value 的形式详细说明报文
- 消息正文 (entity):实际传输的数据,可以是图片,文本,视频等
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”
请求¶
请求行¶
request line,也即 请求报文
的起始行,描述客户端想要如何操作服务器资源
由三部分构成:
- 请求方法:如 GET / POST
- 请求目标:通常是一个 URI
- 版本号:报文使用的 HTTP 协议版本
使用 space 分割,cltf 换行结束,例如:
GET
为请求方法,/
为请求目标,HTTP/1.1
是版本号:需要获取网站根目录下的文件
请求头¶
请求行 / 状态行
+ 头部字段集合
就构成了完整的 请求头 / 响应头
请求头 和 响应头 的结构基本一致,唯一的区别就是起始行。
HTTP 的头字段非常灵活,可以使用标准里的 Host , Connection 等已有头,也可以任意添加自定义头,这给 HTTP 协议带来了拓展性。
头部字段是 key - value 形式,key 和 value 之间使用 : 分隔,最后用 CLTF 换行表示字段结束,例如:
请求体¶
POST 请求的最后一个部分,和头字段使用一个空行隔开
- Get 没有请求体(在请求的资源路径会放参数,请求参数长度有限制)
- Post 参数放在请求体中
响应¶
状态行¶
status line,即 响应报文
的起始行,为服务器响应的状态
由三部分构成:
- 版本号:表示 HTTP 协议版本
- 状态码:三位十进制数,如 200 表示成功,500 是服务器错误
- 原因:作为数字状态码的补充,是更详细的解释文字
例如:
响应头¶
从第二行开始,格式为 KV 结构
响应体¶
存放响应数据(浏览器通过解析这个数据渲染页面)
HTTP/1.1 200 OK
Server: Tengine
Content-Type: text/html
Transfer-Encoding: chunked
<html>
<head>
<title></title>
</head>
<body></body>
</html>
拆分请求行字段¶
HTTP 提供了哪些请求方法¶
标准请求方法¶
HTTP/1.1 中规定了 8 种方法,单词都需要是大写形式:
GET
:获取资源,可以理解为读取或者下载数据;HEAD
:获取资源的元信息;POST
:向资源提交数据,相当于写入或上传数据;PUT
:类似 POST;- DELETE:删除资源;
- CONNECT:建立特殊的连接隧道;
- OPTIONS:列出可对资源实行的方法;
- TRACE:追踪请求 - 响应的传输路径。
服务器对于请求有绝对的决策能力:
比如,你发起了一个 GET 请求,想获取“/orders”这个文件,但这个文件保密级别比较高,服务器就可以有如下的几种响应方式:
- 假装这个文件不存在,直接返回一个 404 Not found 报文
- 稍微友好一点,明确告诉你有这个文件,但不允许访问,返回一个 403 Forbidden
- 再宽松一些,返回 405 Method Not Allowed,然后用 Allow 头告诉你可以用 HEAD 方法获取文件的元信息
GET / HEAD¶
-
GET 方法请求从服务器里获取资源,搭配 URI 和其他头字段能实现对资源的更精细操作。
例如,在 URI 后使用“#”,就可以在获取页面后直接定位到某个标签所在的位置;使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;使用 Range 字段就是“范围请求”,只获取资源的一部分数据
-
HEAD 方法也是请求从服务器获取资源,但服务器不会返回请求的实体数据,只会传回响应头,即资源的 meta data
例如,可以用来检查一个文件是否存在,或者检查文件的最新版本
POST / PUT¶
POST / PUT 是向 URI 指定的资源提交数据,数据放在报文的 body 里
-
POST 通常表示“新建”
例如,上论坛敲了一堆字后点击“发帖”按钮,浏览器就执行了一次 POST 请求,把文字放进报文的 body 里,然后拼好 POST 请求头,通过 TCP 协议发给服务器;上购物网站,点击“加入购物车”,这时也会有 POST 请求,浏览器会把商品 ID 发给服务器,服务器再把 ID 写入你的购物车相关的数据库记录
- PUT 通常表示“修改”
安全与幂等¶
-
安全:请求不会对服务器上的资源造成实质性的修改。
此定义下只有 GET 和 HEAD 方法是安全的
-
幂等:如果多次执行相同的操作,结果也都是相同的
GET / HEAD / DELETE / PUT 幂等
POST 会新建多个资源,不幂等
URI¶
URI 用于标记(区分)服务器上的资源,其英文为 Uniform Resource Identifier
,经常出现在浏览器的地址栏里,但不完全等同于 URL Uniform Resource Locator
,其包含 URL 和 URN 两个部分。HTTP 世界里使用的网址实际上是 URL。
URI 格式¶
URI 是一个字符串,用于唯一地标记资源的位置或者名字
URI 的基本组成¶
-
scheme
:协议名,表示资源应该使用哪种协议来访问最常见的 scheme 即为 "http",表示使用 HTTP 协议。另外还有 "https" ,表示使用经过加密的安全的 HTTPS 协议。还有 "ftp" 等。
-
://
:三个固定的分离字符 -
authority
:表示资源所在的主机名,通常形式为 host:port ,即主机名 + 端口号- 主机名可以是 IP 地址或域名的形式,对于 HTTP / HTTPS 这样的网络通信协议必须要有
-
端口号可以省略,浏览器等客户端会根据
scheme
使用默认的端口号例如 HTTP 的默认端口号为 80,HTTPS 的默认端口号为 443
-
path
:采用类文件系统目录路径的表示方式,path 部分必须以 "/" 开头-
http://nginx.org
省略了端口号(默认为 80),省略了路径(默认为 "/",表示根目录)
-
https://tools.ietf.org/html/rfc7230
路径为 "/html/rfc7230"
-
file:///D:/http_study/www/
注意,这里的协议名为 "file",表示本地文件,这是 file 类型的 URI 的特例:允许省略主机名,默认是本机 localhost
-
客户端URI -> 服务器URI¶
注意,客户端和服务器看到的 URI 是不一样的。
- 客户端(比如浏览器)看到的必须是完整的 URI
- 服务器看到的只是报文请求行里被删除了协议名、主机名以及端口号的 URI
浏览器输入:http://www.chrono.com/11-1
URI 的查询参数¶
使用 协议名
+ 主机名
+ 路径
的方式,已经可以精确定位网络上的任何资源了。但是有时还想再操作资源的时候附加一些额外的参数。
此时在 path
之后用一个 ?
开始,但不包含 ?
表示额外的要求。
例如:
(Chrome 的开发工具也可以解码出 query 里的 KV 对,如果 key 后面不写值就会被解析成空的 value)
URI 的完整格式¶
URI 真正的完整形态如下:
这个形态多出了两个部分:
- 身份信息:在协议名之后,主机名之前,表示登录主机时的身份以及密码(以明文形式暴露了敏感信息,现已不推荐)
- 片段标识符:为浏览器而非服务端提供的一个”标签”,浏览器可以在获取资源后直接跳转到其指示的位置
URI 的编码¶
在 URI 里只能使用 ASCII 码,但是可能出现如下情况:
- 在 URI 里使用汉语等非英语
- 某些特殊的 URI 可能会在
path
/query
里出现 "@" "&" "?" 等起定界作用的字符,会导致 URI 解析错误
解决方法:直接把这些字符转换成十六进制字节值,然后在前面加上 %
例如空格被转义成 "%20" ,将其从地址栏拷贝到其他编辑器中就会现出 % 原型
拆分状态行字段¶
HTTP 状态码¶
服务器返回的状态码(status code)是状态行中的第二个字段,是一个十进制数字,客户端可以根据状态码转换处理状态。
状态码的分类¶
状态码的范围是 100 ~ 599,可以分为 5 类:
-
1 开头:提示信息,表示目前是协议处理的中间状态,还需要后续操作
-
2 开头:成功,报文已经收到并被正确处理
- 200 OK :表示一切正常,如果是非 HEAD 请求,通常响应头后有 body 数据
- 204 No Content :成功,且响应头后没有数据
-
206 Partial Content :是 HTTP 分块下载 / 断点续传的基础。在客户端发送了范围(查询)请求后,服务器成功处理了请求,但是 body 数据里不是资源的全部而是一部分
此处通常还伴随着
头字段
Content-Range,例如 "Content-Range: bytes 0-99/2000" ,表示此次获取了 2000 字节中的前 100 字节
-
3 开头:重定向,资源位置发生变动,需要客户端重新发送请求
- 301 Moved Permanently :此次请求的资源不存在,需要新的 URI 访问
- 302 Moved Tempoarily :请求的资源还在,浏览器会自动重新访问新的页面
- 304 Not Modified :让客户端访问本地的缓存,减轻服务器的压力
-
4 开头:责任在客户端,请求报文有误,服务端无法处理
- 400 Bad Request :请求报文有错误
- 403 Forbidden:无权访问此资源
- 404 Not Found:not found(url 路径错误)
- 405 Method Not Allowed:请求方式有误,比如应该使用 GET,但是使用了 POST
- 其他,可以通过
Reason
原因来理解
-
5 开头:责任在服务端,服务器在处理请求时内部发生了错误
- 500 Internel Server Error
- 502 Bad Gateway :服务器自身工作正常,服务器作为网关或代理返回的错误码
- 503 Service Unavailable :服务器太忙,无法响应
- 511 Network Authentication Required :客户端需要进行身份验证才能访问
HTTP 的连接¶
HTTP 的连接管理¶
短连接¶
HTTP 协议底层的数据传输基于 TCP/IP,每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。整个连接过程很短暂,所以称为“短连接”(short-lived connections)。
注意到三握四挥(1RTT + 2RTT)代价较为昂贵
长连接¶
基于“分母效应”,在一次建立连接与关闭连接之间持续地处理多次请求
长连接的缺点是会消耗服务器的资源,所以长连接也需要在适当时间主动关闭
连接相关头字段¶
注意,由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的 头字段
指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。
如果需要利用长连接机制,也可以在请求头中明确地要求,使用:
Connection: keep-alive
如果需要在此次通信后就关闭连接,可以在请求头中加入:
Connection: close
此时响应报文中也会加上这个字段
如果需要限定长连接的超时时间,客户端与服务器可以在报文中加入头字段,但是其约束力并不强,双方不一定遵守
Keep-Alive: timeout=value
不管客户端是否显式要求长连接,支持长连接的服务器总会在响应报文里放一个
Connection: keep-alive
队头阻塞¶
队头阻塞和短连接 / 长连接无关,由 HTTP 基本的 请求 - 应答
模型导致
HTTP 报文必须是 一发一收,形成了一个先进先出的穿行队列,如果队首的请求因为处理太慢而耽误时间,则后续请求也需要一起等待
性能优化¶
对头阻塞问题在 HTTP/1.1 中无法解决,只能缓解。
- 并发连接:浏览器同时打开多个连接到同一个域名
- 域名分片:注意到浏览器对每个域名最多只能建立一定数量的并发连接。将资源分布到多个子域名上,每个子域名可以独立建立连接
随着 HTTP/2 和 HTTP/3 的出现,这些问题得到了更好的解决,因为它们支持多路复用,可以在一个连接上并行处理多个请求,从而从根本上减少了队头阻塞的影响。
Cookie 和 Session¶
Cookie¶
由服务器创建,浏览器储存,随后的每次请求都会携带 Cookie
xxl job / 定时任务
设计模式相当于一种代码规范