HTTP 定义
超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法
HTTP 报文结构
HTTP 请求报文
现在TCP连接建立完毕,浏览器可以和服务器开始通信,即浏览器开始发送 HTTP 请求。其中请求中要携带三样东西:请求行
、请求头
和请求体
- 请求行 Line (
请求方法method
+请求URL
+HTTP协议版本
)
// 请求方法为POST + 请求URL为/chapter17/user.html + HTTP协议版本为1.1
POST /chapter17/user.html HTTP/1.1
GET
和POST
的区别以及其他请求方式 详见计算机网络系列 -- GET和POST的区别以及其他请求方式
- 请求头 Header (格式为
属性
:属性值
)
Accept: /* 浏览器支持的 MIME 类型 */ text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br /* 浏览器支持的压缩编码 */
Accept-Language: zh-CN,zh;q=0.9 /* 浏览器支持的语言 */
Cache-Control: no-cache /* 不建立缓存 */
If-Modified-Since: xxx /* 与服务器的 Last-Modified 对线 */
If-None-Match: xxx /* 与服务器的 ETag 对线 */
Pragma: no-cache
Cookie: /* 浏览器想服务器发送的cookie内容 */
Connection: keep-alive /* 表示建立持久连接 */
Host: www.baidu.com /* 域名 */
Upgrade-Insecure-Requests: 1
User-Agent: /* 用户代理 */ Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
- 请求体 Body (格式为
param1=value1¶m2=value2
)
请求体只有在POST方法
才存在,常见的场景是表单提交
将一个页面表单中的组件值通过 param1=value1¶m2=value2¶m3=value3 的键值对
形式编码成一个格式化串
,它承载多个请求的参数
HTTP 响应报文
HTTP 请求到达服务器,服务器进行对应的处理。最后要把数据传给浏览器,也就是返回网络响应
,跟请求部分类似,网络响应具有三个部分:响应行
、响应头
和响应体
- 响应行 Line (
HTTP协议版本
+状态码
+状态描述
)
// HTTP协议版本是1.1,状态码是200,状态描述是 OK
HTTP/1.1 200 OK
其它状态码详见 计算机网络系列 -- HTTP状态码
- 响应头 Header(格式为
属性
:属性值
)
Cache-Control: no-cache
Connection: keep-alive /* 表示建立持久链接 */
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Wed, 04 Dec 2019 12:29:13 GMT /* 响应时间 */
Age:/* 响应持续时间 */
Server: apache /* 服务器应用程序软件的名称和版本 */
Set-Cookie: rsv_i=xxx; path=/; domain=.baidu.com /* 服务器下发cookie */
- 响应体 Body(格式为
{"param1":value1,"param2":value2}
) 存的内容就是服务端返回的具体的数据
,格式是字符串
类型,前端需要将其进行JSON.parse()
转换为Object
类型
理解 URI
URI是一种统一资源标识符,作用就是区分
互联网上不同的资源
。
但是,它并不是我们常说的网址, 网址指的是URL, 实际上URI包含了URN和URL两个部分,只不过 URL 过于普及而已。
URI 的结构
URI 真正最完整的结构:
- scheme 表示
协议名
,比如http, https, file等等。后面必须和://连在一起。 - user:passwd@ 表示登录主机时的用户信息,不过很不安全,不推荐使用,也不常用。
- host:port 表示
主机名
和端口
。 - path 表示请求路径,标
记资源所在位置
。 - query 表示查询参数,为key=val这种形式,多个键值对之间用
&
隔开。 - fragment 表示 URI 所定位的资源内的一个
锚点
,浏览器可以根据这个锚点跳转到对应的位置
。 举个例子:
https://www.baidu.com/s?wd=HTTP&rsv_spt=1
这个 URI 中,https即scheme部分,www.baidu.com为host:port部分(注意,http 和 https 的默认端口分别为80、443),/s为path部分,而wd=HTTP&rsv_spt=1就是query部分。
URI 编码
URI 只能使用ASCII
, ASCII 之外的字符是不支持显示的,而且还有一部分符号是界定符,如果不加以处理就会导致解析出错
。
因此,URI 引入了编码
机制,将所有非 ASCII 码字符
和界定符
转为十六进制字节值,然后在前面加个%
。例如:空格被编码成了%20,掘金被编码成了%E6%8E%98%E9%87%91。
HTTP 状态码
关于 HTTP 状态码详见 计算机网络系列 -- HTTP状态码
HTTP 版本及其特点
- HTTP 0.9
- HTTP 1.0
- HTTP 1.1
- HTTP 2.0
HTTP 1.0
- 明文传输 通信使用明文、请求和响应不会对通信方进行确认、无法保护数据的完整性(HTTPS解决)
- 无状态
HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的
每次请求
都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开
TCP连接,很明显这会造成性能上的缺陷
。(HTTP1.1解决)服务器不跟踪每个客户也不记录过去
的请求。(所以需要session+cookie
或token
来作登录态
)
【实际是下一次请求和上一次请求差不多或者有很大的关联性(同一个页面的html、css、js),并不需要断开再连接(需要断开重连是不同页面的html、css、js)】
- 连接无法复用
一般PC端浏览器会针对单个域名的server同时建立
6~8个连接
,手机端的连接数则一般控制在4~6个。显然连接数越多,资源开销和整体延迟会越大。连接无法复用会导致每次请求
都经历三次握手
和慢启动
(HTTP1.1解决)
每发一件货物都要一辆车,每次都要经历:装货、运输、卸货,三个过程
- TCP 队头阻塞问题(head of line blocking)
比如前前后后发送了三个数据包,每次请求建立一个TCP连接,如果
第一个数据包丢失
,将重新建立连接,保证第一个数据包能完整送达,这时后面两个数据包会等待
第一个数据包送达后才建立
TCP连接进行传输,这就造成了“队头阻塞问题
”,一列的第一个数据包(队头)受阻而导致整列数据包受阻
。(TCP本身机制,无法解决)
针对同一区域的请求(比如同一页面的html、css、js),每次发货都要等前一辆车回来才能发下一辆(html对应一辆、css对应一辆、...)这样如果前一辆车的货物很大件,处理贼慢,这样后面的货物就得等很久才能发过去
HTTP 1.1
- 比 HTTP 1.0 新增 流水线技术
- HTTP/1.1之前,由于无状态特点,每次请求需要和服务器重新建立一个TCP连接。HTTP1.1为了解决这个问题,采用了
流水线技术
,也就是说,HTTP 1.1允许客户端不用等待
上一次请求结果返回,就可以发出下一次请求。(一个网页文件的多个请求和应答(比如图像、文件的请求)可以在一个连接中传输,每个单独的网页文件的请求和应答仍然需要使用各自的连接)。 - 但是
服务端
必须按照接收到客户端请求的先后顺序
依次回送响应结果,以保证客户端能够区分出每次请求的响应内容。这表明会出现【HTTP队头阻塞问题
】,意思就是只是缩短了重新建立TCP连接的时间,本质上还是无法做到并行请求
(HTTP2.0解决)
- HTTP/1.1之前,由于无状态特点,每次请求需要和服务器重新建立一个TCP连接。HTTP1.1为了解决这个问题,采用了
`TCP`队头阻塞是`数据包`阻塞
`HTTP`队头阻塞是`HTTP请求`阻塞
- 比 HTTP 1.0 新增 长连接
- 在http1.1中,客户端和服务端都是
默认
对方支持长链接的,就是说,一个TCP连接可以处理多个请求了,而不是每次请求都有重新建立一个TCP连接。这也保证了流水线技术
能够稳稳地流转起来 - 如何设置和关闭长连接:
- 在http1.1中,客户端和服务端都是
客户端在`请求头`设置,服务端在`响应头`设置
Connection: keep-alive // 支持长连接
Connection: close // 关闭长连接
- 比 HTTP 1.0 新增 缓存策略
- http1.0的缓存策略主要是依赖header中的
If-Modiified-Since
,Expires
- http1.1的缓存策略新增例如
ETag
,If-None-Match
等 - 关于缓存策略的详细内容见 浏览器系列 -- 浏览器缓存
- http1.0的缓存策略主要是依赖header中的
- 比 HTTP 1.0 新增 一些状态码
- 关于HTTP状态码的详细内容见 计算机网络系列 -- HTTP状态码
- 比 HTTP1.0 新增 Host请求头字段
- HTTP 1.1中增加Host请求头字段后,WEB浏览器可以使用主机头名来明确表示要访问服务器上的
哪个WEB站点
- 这才实现了在一台WEB服务器上可以在
同一个
IP地址和端口号上使用不同的主机名
来创建多个虚拟WEB站点
- HTTP 1.1中增加Host请求头字段后,WEB浏览器可以使用主机头名来明确表示要访问服务器上的
- 对 HTTP 1.0 宽带和网络连接优化
- http1.0中会存在一些性能浪费,比如我们的只需要对象中的一部分,但是每次请求返回的却是整个对象,这无疑造成了性能的损害
- http1.1则不然,它可以通过在请求头处设置range头域,就可以返回请求资源的
某一部分
,也就是返回码为206
(Partial Content)的时候,这对于性能优化很有必要,这里所谓的请求资源的一部分,也就是大家常说的断点续传。 - 关于
断点续传
的应用场景:
例如用户需要下载`一个大文件`,最佳的方式是将这个大文件分割成几部分,然后由`多个进程同时进行`.
这个时候,我们可以在`请求头`中设置`range`字段,来规定`分割的byte数范围`.
而服务端会给客户端返回一个包含着`content-range`的`响应头`,来对应相应的分割byte数范围
请求头中:
Range: bytes=0-801 // 一般请求下载整个文件是bytes=0- 或不用这个头
响应头中:
Content-Range: bytes 0-800/801 //801:文件总大小
HTTP 2.0
- 多路复用
- 针对 HTTP 1.1 可能出现的
HTTP队头阻塞问题
,HTTP2.0的多路复用也是允许一个TCP连接上发送多个请求,并且请求任务可以并行处理
,不会造成阻塞。如何做到并行处理且不会乱呢?我的理解就是对每个request请求对应一个request_id
作标识,客户端和服务端之间以 request_id 辨别
多重请求-响应,保证不出错
。当然HTTP2.0的底层还是使用TCP协议,仍会出现TCP队头阻塞(TCP本身机制,除非换协议)
- 针对 HTTP 1.1 可能出现的
区分:`HTTP1.1`请求对应的响应会`按序处理`(保证不乱),`不能并行`处理。
而`HTTP2.0`可以实现请求`并行处理`。某个请求任务耗时严重,`不会影响`到其它连接的正常执行
形象区别 流水线技术 和 多路复用
- 最开始是一辆货车只载一件货物,而且每次发件都要等前一辆车的货物处理完,再重新派一辆货车去送
- 流水线技术:一辆货车可以载多几件货物,但是处理货件要按(包装先后)顺序一件一件处理
- 多路复用:一辆货车载多件货物,但是处理货件可以同时处理(按货件号牌作标记)
- 二进制分帧
- HTTP/2在 应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层,在二进制分帧层中, HTTP/2 会将所有传输的信息分割为
更小的消息和帧
,并对它们采用二进制格式
的编码 ,其中 HTTP1.0/1.1 的请求头会被封装到HEADER 帧
,而相应的请求体则封装到DATA 帧
里面
- HTTP/2在 应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层,在二进制分帧层中, HTTP/2 会将所有传输的信息分割为
- 首部压缩
- HTTP 2.0 使用一种专门压缩首部的算法对首部进行压缩,以加快传输速率
- 服务端推送
- 服务端推送是一种在客户端
请求之前
发送数据的机制,就是说在客户端请求之前,服务端会把客户端肯定需要请求的资源(比如 logo 和样式表和脚本文件等)一起顺便
推送过去。 - 服务端推送有个很大的优势就是
可以缓存
。推送缓存让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能
- 服务端推送是一种在客户端
区别:`多路复用`是指一次请求`索要多个`资源(比如css文件、js文件)
`服务端推送`是指一次请求中`只索要`了HTML文件,其中如果【有link-href/@import引入外部css】,
或【script-require引入外部js文件】,然后服务端`顺便`把css文件和js文件一起推送过来
(普通请求示意图)
(服务端推送示意图)
HTTP 3.0
HTTP 2.0 的待优化点在于:
- 建立连接时间长问题
- TCP队头阻塞问题(这在HTTP2.0仍然没有解决)
- 网络拥塞问题
这些问题的本质在于 TCP本身的问题 。所以为了打破这些界限,HTTP 3.0 选择了 UDP
:
首先需要知道 TCP 和 UDP 的区别:
- UDP 的优点:
- UDP 本身是无连接的、没有建链和拆链成本
- UDP 的数据包无队头阻塞问题
- UDP 的缺点:
- UDP 没有控制网络拥塞问题:因为 UDP 始终以
恒定的速率
发送数据,并不会根据网络拥塞情况对发送速率作调整。因此要是出现丢包也没有阻塞,也就意味着有可能会出现丢包现象,这样就不可靠,也不安全
- UDP 没有控制网络拥塞问题:因为 UDP 始终以
如何让 UDP 继承 TCP 的强大功能,又能修复 TCP 所带来的问题呢?
HTTP 3.0 采用了基于 UDP 协议的 QUIC 协议:
- 继承
- 继承多路复用的能力;
- 继承首部压缩的能力;
- 继承网络拥塞控制的能力:虽然不控制速率,但 UDP 有多条道,在某一条道上出现阻塞问题的时候就再建一条道,出车之前就提前预估一下发送速率要多少最佳;
- 修复:
- 解决连接时间长问题:无连接;
- 解决队头阻塞问题:TCP 只有一个通道,而 UDP 有多个通道,不需要控制速率,不会有阻塞问题(TCP 的多路复用是指的一个通道走车,车里有多个“货件”(HTTP 请求));
更多关于 TCP 和 UDP 的区别在:计算机网络系列 -- TCP和UDP
HTTP 通信
HTTP 通信流程示意图
具体通信过程见 计算机网络系列 -- 从URL到网页加载
HTTP属于TCP/IP模型中的运用层协议,所以通信的过程其实是对应数据的入栈
和出栈
图中少了物理层
,我理解这是硬件的相关内容
各种协议之间的关系:
- 发送端每通过一层
增加
一层首部;接收端每通过一层减少
一层首部,总共有【五】层协议 - HTTP / HTTPS 是
应用层
的协议 - TCP / UDP 是
传输层
的协议 - IP 是
网络层
的协议 - 以太网 是
链路层
的 - 传输媒介是
物理层
的
传送门
HTTPS的详细内容见 计算机网络系列 -- HTTPS