计算机网络系列 -- HTTP

409 阅读13分钟

HTTP 定义

超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法

HTTP 报文结构

HTTP 请求报文

现在TCP连接建立完毕,浏览器可以和服务器开始通信,即浏览器开始发送 HTTP 请求。其中请求中要携带三样东西:请求行请求头请求体

image.png

  • 请求行 Line (请求方法method + 请求URL + HTTP协议版本
// 请求方法为POST + 请求URL为/chapter17/user.html + HTTP协议版本为1.1
POST  /chapter17/user.html  HTTP/1.1

GETPOST的区别以及其他请求方式 详见计算机网络系列 -- 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&param2=value2

请求体只有在POST方法才存在,常见的场景是表单提交

将一个页面表单中的组件值通过 param1=value1&param2=value2&param3=value3 的键值对形式编码成一个格式化串,它承载多个请求的参数

HTTP 响应报文

HTTP 请求到达服务器,服务器进行对应的处理。最后要把数据传给浏览器,也就是返回网络响应,跟请求部分类似,网络响应具有三个部分:响应行响应头响应体

image.png

  • 响应行 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 真正最完整的结构: image.png

  • 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+cookietoken 来作登录态

【实际是下一次请求和上一次请求差不多或者有很大的关联性(同一个页面的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解决)
`TCP`队头阻塞是`数据包`阻塞
`HTTP`队头阻塞是`HTTP请求`阻塞
  • 比 HTTP 1.0 新增 长连接
    • 在http1.1中,客户端和服务端都是默认对方支持长链接的,就是说,一个TCP连接可以处理多个请求了,而不是每次请求都有重新建立一个TCP连接。这也保证了流水线技术能够稳稳地流转起来
    • 如何设置和关闭长连接:
客户端在`请求头`设置,服务端在`响应头`设置
Connection: keep-alive // 支持长连接
Connection: close // 关闭长连接
  • 比 HTTP 1.0 新增 缓存策略
    • http1.0的缓存策略主要是依赖header中的 If-Modiified-SinceExpires
    • http1.1的缓存策略新增例如 ETagIf-None-Match
    • 关于缓存策略的详细内容见 浏览器系列 -- 浏览器缓存
  • 比 HTTP 1.0 新增 一些状态码
  • 比 HTTP1.0 新增 Host请求头字段
    • HTTP 1.1中增加Host请求头字段后,WEB浏览器可以使用主机头名来明确表示要访问服务器上的哪个WEB站点
    • 这才实现了在一台WEB服务器上可以在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟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本身机制,除非换协议)
区分:`HTTP1.1`请求对应的响应会`按序处理`(保证不乱),`不能并行`处理。
而`HTTP2.0`可以实现请求`并行处理`。某个请求任务耗时严重,`不会影响`到其它连接的正常执行

形象区别 流水线技术 和 多路复用

  • 最开始是一辆货车只载一件货物,而且每次发件都要等前一辆车的货物处理完,再重新派一辆货车去送
  • 流水线技术:一辆货车可以载多几件货物,但是处理货件要按(包装先后)顺序一件一件处理
  • 多路复用:一辆货车载多件货物,但是处理货件可以同时处理(按货件号牌作标记)

image.png

  • 二进制分帧
    • HTTP/2在 应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层,在二进制分帧层中, HTTP/2 会将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码 ,其中 HTTP1.0/1.1 的请求头会被封装到 HEADER 帧,而相应的请求体则封装到 DATA 帧 里面
  • 首部压缩
    • HTTP 2.0 使用一种专门压缩首部的算法对首部进行压缩,以加快传输速率
  • 服务端推送
    • 服务端推送是一种在客户端请求之前发送数据的机制,就是说在客户端请求之前,服务端会把客户端肯定需要请求的资源(比如 logo 和样式表和脚本文件等)一起顺便推送过去。
    • 服务端推送有个很大的优势就是可以缓存。推送缓存让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能
区别:`多路复用`是指一次请求`索要多个`资源(比如css文件、js文件)
`服务端推送`是指一次请求中`只索要`HTML文件,其中如果【有link-href/@import引入外部css】,
或【script-require引入外部js文件】,然后服务端`顺便`把css文件和js文件一起推送过来

(普通请求示意图) image.png (服务端推送示意图) image.png

HTTP 3.0

HTTP 2.0 的待优化点在于:

  • 建立连接时间长问题
  • TCP队头阻塞问题(这在HTTP2.0仍然没有解决)
  • 网络拥塞问题

这些问题的本质在于 TCP本身的问题 。所以为了打破这些界限,HTTP 3.0 选择了 UDP

首先需要知道 TCP 和 UDP 的区别:

  • UDP 的优点:
    • UDP 本身是无连接的、没有建链和拆链成本
    • UDP 的数据包无队头阻塞问题
  • UDP 的缺点:
    • UDP 没有控制网络拥塞问题:因为 UDP 始终以恒定的速率发送数据,并不会根据网络拥塞情况对发送速率作调整。因此要是出现丢包也没有阻塞,也就意味着有可能会出现丢包现象,这样就不可靠,也不安全

如何让 UDP 继承 TCP 的强大功能,又能修复 TCP 所带来的问题呢?

HTTP 3.0 采用了基于 UDP 协议的 QUIC 协议:

  • 继承
    1. 继承多路复用的能力;
    2. 继承首部压缩的能力;
    3. 继承网络拥塞控制的能力:虽然不控制速率,但 UDP 有多条道,在某一条道上出现阻塞问题的时候就再建一条道,出车之前就提前预估一下发送速率要多少最佳;
  • 修复:
    1. 解决连接时间长问题:无连接;
    2. 解决队头阻塞问题:TCP 只有一个通道,而 UDP 有多个通道,不需要控制速率,不会有阻塞问题(TCP 的多路复用是指的一个通道走车,车里有多个“货件”(HTTP 请求));

更多关于 TCP 和 UDP 的区别在:计算机网络系列 -- TCP和UDP

HTTP 通信

HTTP 通信流程示意图

image.png

具体通信过程见 计算机网络系列 -- 从URL到网页加载

HTTP属于TCP/IP模型中的运用层协议,所以通信的过程其实是对应数据的入栈出栈

image.png

图中少了物理层,我理解这是硬件的相关内容

各种协议之间的关系:

  • 发送端每通过一层增加一层首部;接收端每通过一层减少一层首部,总共有【五】层协议
  • HTTP / HTTPS 是应用层的协议
  • TCP / UDP 是传输层的协议
  • IP 是网络层的协议
  • 以太网 是链路层
  • 传输媒介是物理层

传送门

HTTPS的详细内容见 计算机网络系列 -- HTTPS

参考文章