HTTP 协议的前世今生
1、HTTP 的基本概念
HTTP 是超文本传输协议(HyperText Transfer Protocol),下面具体解释一下:
- 协议:HTTP 是一个用在计算机世界里的协议,它使用计算机能够理解的语言确立了一种计算机之间交流通信的方式(两个及以上的参与者),以及相关的各种控制和错误处理方式(行为约定和规范)。
- 传输:HTTP 是一个双向协议,比如 A 浏览器访问 B 服务器,使用 HTTP 协议来通信,数据就在 A 和 B 之间传输,但是中间允许有中转或接力。HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。其中两点不局限于浏览器到服务器,也可以是服务器到服务器。
- 超文本:包含了文字、图片、视频等,关键是有超链接,不再局限于简单的文本字符,例如 HTML 就是常见的超文本了。
总结:HTTP 是一个在计算机世界里面专门在两点之间传输文字、图片、视频等超文本数据的约定和规范。
2、HTTP 的版本
HTTP/0.9:HTTP 于 1990 年问世,功能简陋,仅支持 Get 请求方式,并且仅能访问 HTML 格式的资源,并没有被作为正式的标准被建立。
HTTP/1.0:在 0.9 的版本上增加了请求方式 Post 和 Head,不再局限于 HTML 格式,根据 Content-Type 支持多种数据格式,但其工作方式是短连接。
HTTP/1.1:相比于 1.0 版本引入了长连接以及流水线机制(管道机制),但是 HTTP/1.1 还是有性能瓶颈。
- 请求/响应头部未经压缩就发送,首部信息越多延迟越大
- 发送冗长的首部,每次互相发送相同的首部造成的浪费越多
- 服务器是按请求的顺序响应的,如果服务器响应慢,导致客户端一直请求不到数据,也就是队头阻塞
- 没有请求优先级控制
- 请求只能从客户端开始,服务器只能被动响应
HTTP/2:相比于 HTTP/1.1 有性能上的改进:
- 头部压缩:HTTP/2 会压缩头,如果同时发出多个请求,它们的头是一样的或是相似的,协议会帮你消除重复的部分。
- 二进制格式:HTTP/2 不再像 HTTP/1.1 里的村文本格式,而是采用了二进制格式,头信息和数据体都是二进制,并且统称为帧:头信息帧和数据帧。增加了信息传输的效率。
- 数据流:HTTP/2 的数据包不是按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。每个请求或回应的所有数据包,称为一个数据流(
Stream
)。每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数客户端还可以指定数据流的优先级。优先级高的请求,服务器就先响应该请求。 - 多路复用:HTTP/2 是可以在一个连接中并发多个请求或回应,而不用按照顺序一一对应。移除了 HTTP/1.1 中的串行请求,不需要排队等待,也就不会再出现「队头阻塞」问题,降低了延迟,大幅度提高了连接的利用率。
- 服务器推送:HTTP/2 还在一定程度上改善了传统的「请求 - 应答」工作模式,服务不再是被动地响应,也可以主动向客户端发送消息。
3、HTTP请求和响应
当在浏览器中输入网址访问某个网站时,浏览器(客户端)会将你的请求封装成一个 HTTP 请求发送给服务器站点,服务器接收到请求后会组织响应数据封装成一个 HTTP 响应返回给浏览器。
3.1、HTTP 请求报文
HTTP 请求报文由 3 大部分组成:
- 请求行(必须在 HTTP 请求报文的第一行):包括请求方法,URI,HTTP 版本
- 请求头(第二行开始,到第一个空行结束。请求头和请求体之间存在一个空行)
- 请求体(通常以键值对的方式传递数据,并不是必须的)
报文中的方法是请求服务器的请求类型,URI 指明了访问的资源对象,版本是 HTTP 的版本。
请求方法
请求行中的方法的作用在于可以指定请求的资源按照期望产生某种行为,即使用方法给服务器下命令。HTTP 1.1 开始包含如下请求方法:GET、POST、PUT、HEAD、DELETE、OPTIONS、CONNECT、TRACE,最常见的就是前三个了。
GET 获取资源:GET 方法用来请求访问已被 URI 识别的资源。指定的资源经服务器端解析后返回响应内容,简单的说就是获取服务器资源。
POST 传输实体主体:POST 主要用来传输数据,比如浏览器传输数据给服务器,就是用 POST 方法
PUT 传输文件:PUT 方法用来传输文件,由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。
HEAD 获取报文首部:和 GET 方法类似,但是不返回报文实体主体部分,主要用于确认 URI 有效性以及资源更新的日期时间等。
HELETE 删除文件:与 PUT 功能相反,用来删除文件,并且同样不带验证机制,按照请求 URI 删除指定的资源。
OPTIONS 查询支持的方法:用于获取当前 URI 所支持的方法。若请求成功,会在 HTTP 响应头中包含一个名为 “Allow” 的字段,值是所支持的方法。
请求头
请求头用于补充请求的附加信息、客户端信息、对响应内容相关的优先级等内容,常见的请求头如下:
Referer:用于表示这个请求是从哪个 URI 跳过来的,通常用于防盗链。
Accept:告诉服务端,该请求所能支持的响应数据类型,如果服务端返回的数据类型和 Accept 指定的类型不一致,就会报错。常见的数据类型有:text/html、text/plain、text/xml 等等。
Host:告知服务器请求的资源所处的互联网主机名和端口号,是 1.1 版本中唯一必须包含在请求头中的字段。
Connection:表示客户端与服务器连接类型,Keep-Alive 表示持久连接,close 已关闭。
Content-Length:请求体的长度
Accept-Language:浏览器通知服务器,浏览器支持的语言
Range:对于只获取部分资源的请求范围,包含首部字段 Range 即可告知服务器资源的指定范围。
3.2、HTTP 响应报文
HTTP 响应报文也由三部分组成:
- 响应行(必须在 HTTP 响应报文的第一行):协议版本、状态码、原因短语
- 响应头(从第二行开始,到第一个个空行结束。响应头和响应体之间存在一个空行)
- 响应体
HTTP 状态码
HTTP 状态码负责表示客户端 HTTP 请求的返回结果、标记服务器端处理是否正常、通知出现的错误等工作。
状态码 | 类别 | 原因短语 |
---|---|---|
1xx | Informational 信息性状态码 | 接收的请求正在处理 |
2xx | Success成功状态码 | 请求正常处理完毕 |
3xx | Redirection 重定向状态码 | 需要进行附加操作以完成请求 |
4xx | Client Error 客户端错误状态码 | 服务器无法处理请求 |
5xx | Server Error 服务器错误状态码 | 服务器处理请求出错 |
2xx:请求正常处理完毕
- 200 OK:客户端请求成功
- 204 No Content:无内容,服务器处理成功,但是未返回内容
- 206 Partial Content:服务器已经完成了部分 GET 请求(客户端进行了范围请求),响应报文总包含 Content-Range 指定范围的实体内容。
3xx:需要进行附加操作以完成请求(重定向)
- 301 Moved Permanently:永久重定向
- 302 Found:临时重定向
- 303 See Other:临时重定向,应使用 GET 定向获取请求资源
- 304 Not Modified:表示客户端发送附带条件的请求,条件不满足时,返回304,不包含响应主体,和重定向没关系。
4xx:客户端错误
- 400 Bad Request:客户端请求有语法错误,服务器无法理解
- 401 Unauthorized:请求未经授权,这个状态码必须和 WWW-Authenticate 报头域一起使用
- 403 Forbidden:服务器收到请求,但是拒绝提供服务
- 404 Not Found:请求资源不存在,比如输入了错误的 URL
- 415 Unsupported media type:不支持的媒体类型
5xx:服务端错误,服务器未能实现合法的请求
- 500 Internal Server Error:服务器发生不可预期的错误。
- 501 Not Implemented:表示客户端请求的功能还不支持
- 502 Bad Gateway:服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务发生了错误
- 503 Server Unavailable:服务器当前处于超负载或停机维护状态,暂时不能处理客户端请求,一段时间后可能恢复正常
响应头
响应头也是键值对,用于补充响应的附加信息、服务器信息,以及对客户端的附加要求等。
Allow:服务器支持哪些请求方法(如 GET、POST)
Content-Encoding:文档的编码,只有在解码之后才能得到 Content-Type 头指定的内容类型。
Content-Length:表示内容长度,只有浏览器使用持久连接时才需要这个数据。
Content-Type:表示后面的文档属于什么 MIME 类型。
Location:表示客户应到哪里去提取文档,一般和重定向一起使用。
4、HTTP 连接管理
短连接(非持久连接)
在 HTTP 协议的初始版本(HTTP/1.0)中,客户端和服务器每进行一次 HTTP 会话,就建立一次连接,任务结束就中断连接。当客户端浏览器每访问一个资源,浏览器就会重新建立一个 HTTP 会话,这种方式称为短连接,也称非持久连接。
由于 HTTP 是基于 TCP/IP 协议的,所以连接的每一次建立或断开都需要 TCP 三次握手或者 TCP 四次挥手的开销。
这种连接方式,大大增加了通信量的开销。
长连接(持久连接)
从 HTTP/1.1 开始,默认使用长连接也称持久连接 keep-alive,使用长连接的 HTTP 协议,会在响应头加入这行代码:Connection:keep-alive。
在使用长连接的情况下,当一个网页打开完之后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器的时候,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件中设定这个时间。实现长连接需要客户端和服务器都支持长连接。
HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。
流水线(管线化)
默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出,由于受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前可能需要等待很长时间。
持久连接使得多数请求以流水线(管线化 pipeline)方式发送成为可能,即在同一条持久连接上连续发出请求,而不用等待响应返回后再发送,这样可以做到同时并行发送多个请求,不需要一个接一个等待响应了。
5、HTTP 的缺点
无状态的 HTTP协议
HTTP 协议是无状态协议,也就是说它不对之前发生过的请求和响应的状态进行管理,即无法根据之前的状态进行本次的请求处理。
这样会带来一个问题,如果 HTTP 无法记住用户登录的信息,那岂不是每次页面的跳转都会导致用户需要再次登录?
但是无转态也有优点,不必保存状态,减少了服务器的 CPU 及内存资源的消耗,另一方面,由于 HTTP 简单,才会被广泛应用。
那怎么解决无状态导致的问题呢?
比较简单的方式就是使用 Cookie,Cookie 通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。Cookie 会根据从服务器发送的响应报文中的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存 Cookie。当下次客户端再往服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值发送出去。服务器收到客户端发来的 Cookie 后,会检查究竟哪个客户端发送来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
明文传输
明文意味着在传输过程中的信息,是可方便阅读的,但是这样 HTTP 的所有信息都暴露在了光天化日之下,相当于信息裸奔。在传输的过程中,信息的内容都无隐私可言,很容易被窃取。
不安全
- 通信使用明文(不加密),内容可能会被窃听
- 不验证通信方的身份,可能遭到伪装
- 无法什么报文的完整性,可能被篡改
HTTP 的安全问题,可以使用 HTTPS 的方式解决,也就是通过引入 SSL/TLS 层,使得在安全上达到了极致。
巨人的肩膀: