深入学习HTTP协议


HTTP是什么?HTTP又不是什么?

简单来说HTTP是超文本传输协议,所以可以拆分成超文本、传输、协议

从协议上看

HTTP 是一个用在计算机世界里的协议。它使用计算机能够理解的语言确立了一种计算机之
间交流通信的规范,以及相关的各种控制和错误处理方式。

传输

HTTP 是一个“传输协议”:

  1. HTTP 协议是一个“双向协议“。
  2. 允许中间有“中转”或者“接力”:“A<===>B”=“A<=>X<=>Y<=>Z<=>B”

总的来收,HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。

超文本

所谓“超文本”,就是“超越了普通文本的文本”,例如我们最熟悉的HTML

三者结合来说

HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范


HTTP 不是什么

  1. 不存在“单独的实体”
  2. HTTP 不是互联网
  3. HTTP 不是编程语言,HTTP 不是 HTML
  4. HTTP 不是一个孤立的协议

总的来说

  1. HTTP 是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以
    及相关的各种控制和错误处理方式。
  2. HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。
  3. HTTP 传输的是文字、图片、音频、视频等超文本数据。
  4. HTTP 是构建互联网的重要基础技术,它没有实体,依赖许多其他的技术来实现,但同
    时许多技术也都依赖于它。

HTTP世界全览(上):与HTTP相关的各种概念

浏览器“Web Browser”

浏览器本质上是一个 HTTP 协议中的请求方

小结

  1. 互联网上绝大部分资源都使用 HTTP 协议传输;
  2. 浏览器是 HTTP 协议里的请求方,即 User Agent;
  3. 服务器是 HTTP 协议里的应答方,常用的有 Apache 和 Nginx;
  4. CDN 位于浏览器和服务器之间,主要起到缓存加速的作用;
  5. 爬虫是另一类 User Agent,是自动访问网络资源的程序。

HTTP世界全览(下):与HTTP相关的各种协议

  1. TCP/IP 是网络世界最常用的协议,HTTP 通常运行在 TCP/IP 提供的可靠传输基础上;
  2. DNS 域名是 IP 地址的等价替代,需要用域名解析实现到 IP 地址的映射;
  3. URI 是用来标记互联网上资源的一个名字,由“协议名 + 主机名 + 路径”构成,俗称
    URL;
  4. HTTPS 相当于“HTTP+SSL/TLS+TCP/IP”,为 HTTP 套了一个安全的外壳;
  5. 代理是 HTTP 传输过程中的“中转站”,可以实现缓存加速、负载均衡等功能。

常说的“四层”和“七层”到底是什么?“五层”“六层”哪去了?

OSI 网络分层模型

  1. OSI,全称是“开放式系统互联通信参考模型”

  2. 所谓的“四层负载均衡”就是指工作在传输层上,基于 TCP/IP 协议的特性,例如 IP 地址、端口号等实现对后端服务器的负载均衡。

  3. 所谓的“七层负载均衡”就是指工作在应用层上,看到的是 HTTP 协议,解析 HTTP 报文里的 URI、主机名、资源类型等数据,再用适当的策略转发给后端服务器。


TCP/IP 协议栈的工作方式

  1. HTTP 协议的传输过程就是这样通过协议栈逐层向下,每一层都添加本层的专有数据,层层打包,然后通过下层发送出去。(相当于你寄快递的过程,比如你要寄一个玩具,玩具就是内容,包装就是TCP头,然后快递小哥拿着你的快递到达快递中心,进行打包,这个”包“就是MAC头和IP头,到达目的地,拆包(清除MAC头和IP头),然后送到目的人手上,他再拆包裹,就是卸掉TCP头,最后拿到的就是真正的内容(相当于你请求网页的内容))

小结

  1. TCP/IP 分为四层,核心是二层的 IP 和三层的 TCP,HTTP 在第四层;
  2. OSI 分为七层,基本对应 TCP/IP,TCP 在第四层,HTTP 在第七层;
  3. OSI 可以映射到 TCP/IP,但这期间一、五、六层消失了;
  4. 日常交流的时候我们通常使用 OSI 模型,用四层、七层等术语;
  5. HTTP 利用 TCP/IP 协议栈逐层打包再拆包,实现了数据传输,但下面的细节并不可见。
  6. 有一个辨别四层和七层比较好的(但不是绝对的)小窍门,“两个凡是”凡是由操作系统
    负责处理的就是四层或四层以下,否则,凡是需要由应用程序(也就是你自己写代码)负责
    处理的就是七层

域名里有哪些门道?

域名的形式

  1. 最右边的被称为“顶级域名”,然后是“二级域名”,层级关系向左依次降低。

域名的解析(树形查询)

例如,你要访问“www.apple.com”,就要进行下面的三次查询:

  1. 访问根域名服务器,它会告诉你“com”顶级域名服务器的地址;
  2. 访问“com”顶级域名服务器,它再告诉你“apple.com”域名服务器的地址;
  3. 最后访问“apple.com”域名服务器,就得到了“www.apple.com”的地址。
解决域名访问慢的关键思路:缓存

浏览器缓存->操作系统缓存->hosts->dns

小结

  1. 域名使用字符串来代替 IP 地址,方便用户记忆,本质上一个名字空间系统;
  2. DNS 就像是我们现实世界里的电话本、查号台,统管着互联网世界里的所有网站,是一
    个“超级大管家”;
  3. DNS 是一个树状的分布式查询系统,但为了提高查询效率,外围有多级的缓存;
  4. 使用 DNS 可以实现基于域名的负载均衡,既可以在内网,也可以在外网。

自己动手,搭建HTTP实验环境

  1. 现实的网络环境太复杂,有很多干扰因素,搭建“最小化”的环境可以快速抓住重点,
    掌握 HTTP 的本质;
  2. 我们选择 Wireshark 作为抓包工具,捕获在 TCP/IP 协议栈中传输的所有流量;
  3. 我们选择 Chrome 或 Firefox 浏览器作为 HTTP 协议中的 user agent;
  4. 我们选择 OpenResty 作为 Web 服务器,它是一个 Nginx 的“强化包”,功能非常丰
    富;
  5. Telnet 是一个命令行工具,可用来登录主机模拟浏览器操作;
  6. 在 GitHub 上可以下载到本专栏的专用项目源码,只要把 OpenResty 解压到里面即可完
    成实验环境的搭建。

键入网址再按下回车,后面究竟发生了什么?

  1. 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
  2. 浏览器用 TCP 的三次握手与服务器建立连接;
  3. 浏览器向服务器发送拼好的报文;
  4. 服务器收到报文后处理请求,同样拼好报文再发给浏览器;
  5. 浏览器解析报文,渲染输出页面。

小结

  1. HTTP 协议基于底层的 TCP/IP 协议,所以必须要用 IP 地址建立连接;
  2. 如果不知道 IP 地址,就要用 DNS 协议去解析得到 IP 地址,否则就会连接失败;
  3. 建立 TCP 连接后会顺序收发数据,请求方和应答方都必须依据 HTTP 规范构建和解析报
    文;
  4. 为了减少响应时间,整个过程中的每一个环节都会有缓存,能够实现“短路”操作;
  5. 虽然现实中的 HTTP 传输过程非常复杂,但理论上仍然可以简化成实验里的“两点”模
    型。

HTTP报文是什么样子的

报文结构

不过与 TCP/UDP 不同的是,它是一个“纯文本”的协议,所以头数据都是 ASCII 码的文本(比TCP/UDP易读)

header
  1. HTTP的header一般都很大,HTTP 报文经常是只有 header 而没 body
  2. 不过这个“大头”也不能太大,虽然 HTTP 协议对 header的大小没有做限制
请求行

描述了客户端想要如何操作服务器端的资源。GET / HTTP/1.1,由请求方法,请求目标,版本号组成

状态行

服务器响应的状态。HTTP/1.1 200 OK,由版本号,状态码,和原因组成

头部字段

唯一的区别是起始行

GET /09-1 HTTP/1.1
Host: www.chrono.com

GET /09-1 HTTP/1.1
Host : www.chrono.com

Host后面一定要接着:,不然就会报400 Bad Request

小结

  1. HTTP 报文结构就像是“大头儿子”,由“起始行 + 头部 + 空行 + 实体”组成,简单地说就是“header+body”;
  2. HTTP 报文可以没有 body,但必须要有 header,而且header 后也必须要有空行,形象地说就是“大头”必须要带着“脖子”;
  3. 请求头由“请求行 + 头部字段”构成,响应头由“状态行 + 头部字段”构成;
  4. 请求行有三部分:请求方法,请求目标和版本号;
  5. 状态行也有三部分:版本号,状态码和原因字符串;
  6. 头部字段是 key-value 的形式,用“:”分隔,不区分大小写,顺序任意,除了规定的标准头,也可以任意添加自
    定义字段,实现功能扩展;
  7. HTTP/1.1 里唯一要求必须提供的头字段是 Host,它必须出现在请求头里,标记虚拟主机名。

应该如何理解请求方法

目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式:

  1. GET:获取资源,可以理解为读取或者下载数据;
  2. HEAD:获取资源的元信息;
  3. POST:向资源提交数据,相当于写入或上传数据;
  4. PUT:类似 POST;
  5. DELETE:删除资源;
  6. CONNECT:建立特殊的连接隧道;
  7. OPTIONS:列出可对资源实行的方法;
  8. TRACE:追踪请求 - 响应的传输路径。

请求其实就相当于指示,由客户端发出,但是决定权在服务端那里

GET和HEAD

HEAD与GET类试,但是HEAD只获取资源的元信息,HEAD被看成是GET的简化版。例如:比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。

POST/PUT

PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义

安全与幂等

在 HTTP 协议里,所谓的“安全”是指请求方法不会“破”服务器上的资源即不会对服务器上的资源造成实质的
修改。例如,GET和HEAD是获取,不会造成损害,而POST和PUT就会修改资源。

所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。

POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的

PUT是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的

POST 理解成 INSERT,把 PUT 理解成 UPDATE

小结

  1. 请求方法是客户端发出的、要求服务器执行的、对资源的一种操作;
  2. 请求方法是对服务器的“指示”,真正应如何处理由服务器来决定;
  3. 最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据;
  4. HEAD 方法是轻量级的 GET,用来获取资源的元信息;
  5. PUT 基本上是 POST 的同义词,多用于更新数据;
  6. “安全”与“幂等”是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统。

你能写出正确的网址吗?

URI 不完全等同于网址,它包含有 URLURN两个部分.网址实际上是 URL

URI 的格式

URI 本质上是一个字符串,这个字符串的作用是唯一地标记资源的位置或者名字

URI 的 path 部分必须以“/”开始,也就是必须包含“/”,不要把“/”误认为属于前面 authority。

我们还得到了一个结论:客户端和服务器看到的 URI 是不一样的,服务器看到的只是报文请求行里被删除了协议名和主机名的 URI。所以,你在服务端写的GetMapping全都是写path和后续的部分

URI 的查询参数

这个就不做过多解释,也就是在path后面加?就是参数的开始(不包括?),用&符号连接key和value

URI 的完整格式

主机名之前的**身份信息“user:passwd@”**因为它把敏感信息以明文形式暴露出来,存在严重的安全隐患(一斤不使用了)

第二个多出的部分是查询参数后的片段标识符“#fragment”。片段标识符仅能由浏览器这样的客户端使用,服务器是看
不到的。所以也不会存在

URI 的编码

URI 转义的规则有点“简单粗暴”,直接把非 ASCII 码或特殊字符转换成十六进制字节值,然后前面再加上一个“%”

小结

  1. URI 是用来唯一标记服务器上资源的一个字符串,通常也称为 URL;

  2. URI 通常由 scheme、host:port、path 和 query 四个部分组成,有的可以省略;

  3. scheme 叫**“方案名”或者“协议名”**,表示资源应该使用哪种协议来访问;

  4. “host:port”表示资源所在的主机名和端口号

  5. path 标记资源所在的位置;

  6. query 表示对资源附加的额外要求

  7. 在 URI 里对“@&/”等特殊字符和汉字必须要做编码,否则服务器收到 HTTP 报文后会无法正确处理。

  8. HTTP 协议允许在在请求行里使用完整的 URI,但为什么浏览器没有这么做呢?

    没有必要,因为在请求头的字段中都有,没必要重复

  9. URI 的查询参数和头字段很相似,都是 key-value 形式,都可以任意自定义,那么它们在使用时该如何区别呢?

    query参数针对的是资源(uri),而字段针对的是本次请求,也就是报文。
    一个是长期、稳定的,一个是短期、临时的

响应状态码该怎么用?

Reason 部分是原因短语。状态码,以代码的形式表示服务器对请求的处理结果

状态码

这五类的具体含义是:
1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
2××:成功,报文已经收到并被正确处理;
3××:重定向,资源位置发生变动,需要客户端重新发送请求;

4××:客户端错误,请求报文有误,服务器无法处理;
5××:服务器错误,服务器在处理请求时内部发生了错误。

小结

  1. 状态码在响应报文里表示了服务器对请求的处理结果;
  2. 状态码后的原因短语是简单的文字描述,可以自定义;
  3. 状态码是十进制的三位数,分为五类,从 100 到 599;
  4. 2××类状态码表示成功,常用的有 200、204、206;
  5. 3××类状态码表示重定向,常用的有 301、302、304;
  6. 4××类状态码表示客户端错误,常用的有 400、403、404;
  7. 5××类状态码表示服务器错误,常用的有 500、501、502、503。

HTTP有哪些特点

  1. 灵活可扩展
  2. 可靠传输因为 HTTP 协议是基于 TCP/IP 的,而 TCP 本身是一个“可靠”的传输协议,所以 HTTP 自然也就继承了这个特性。“可靠”只是向使用者提供了一个“承诺”,会在下层用多种手段“尽量”保证数据的完整送达。并不是100%一定可以发送到另一端
  3. 应用层协议
  4. 请求 - 应答,“一发一收”“有来有去”
  5. 无状态,客户端和服务器永远是处在一种“无知”的状态,“没有记忆能力”

但不要忘了 HTTP 是“灵活可扩展”的,虽然标准里没有规定“状态”,但完全能够在协议的框架里给它“打个补丁”,增加这个特性。

小结

  1. HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能;(优点)

  2. HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达;(优点)

  3. HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据;(优点)

  4. HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;(注定是不会再IM场景(即时通信)下使用,因为即时通信,需要服务端主动推送给客户端消息),所以就会萌生出websocket协议

  5. HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。比如,在一些长连接场景中,需要保存上下文状态,那么无状态这一点就成为缺点甚至是致命缺点了。但是在客户端-服务端通信中,如果场景不需要保存上下文信息,那么无状态就可以减少一些网络资源消耗,也就是优点了。


HTTP有哪些优点?又有哪些缺点?

  1. 简单、灵活、易于扩展(最重要也是最突出),容易上手,heder字段,状态码,还有body数据等都可以自己修改和扩展
  2. 应用广泛、环境成熟从台式机上的浏览器到手机上的各种 APP,从看新闻、泡论坛到购物、理财、“吃鸡”,你很难找到一个没有使用 HTTP 的地方。
  3. 无状态
    1. 优点:所以就不需要额外的资源来记录状态信息,不仅实现上会简单一些,而且还能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。很容易地组成集群(负载均衡),一位内服务器都是相同的(无状态)。不会因为状态不一致导致处理出错。
    2. 缺点无法支持需要连续多个步骤的“事务”操作,例如电商项目中的,添加购物车其实是要验证登陆的(要知道用户的身份),但是,如果每次都询问一遍身份信息就太麻烦了,还要增加了数据的传输量从而有了cookie技术
  4. 明文,协议里的报文(准确地说是 header 部分)不使用二进制数据,而是用简单可阅读的文本形式
    1. 优点:为我们的开发调试工作带来极大的便利
    2. 缺点毫无隐私可言
  5. 不安全,与“明文”缺点相关但不完全等同的另一个缺点是“不安全”。HTTP 协议也不支持“完整性校验”,数据在传输过程中容易被窜改而无法验证真伪。为了解决 HTTP 不安全的缺点,所以就出现了 HTTPS,这个我们以后再说。
  6. 性能不算差,不够好,当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据

小结

  1. HTTP 最大的优点是简单、灵活和易于扩展
  2. HTTP 拥有成熟的软硬件环境应用的非常广泛,是互联网的基础设施;
  3. HTTP 是无状态的,可以轻松实现集群化,扩展性能,但有时也需要用 Cookie 技术来实现“有状态”;
  4. HTTP 是明文传输,数据完全肉眼可见,能够方便地研究分析,但也容易被窃听;
  5. HTTP 是不安全的,无法验证通信双方的身份,也不能判断报文是否被窜改;
  6. HTTP 的性能不算差,但不完全适应现在的互联网,还有很大的提升空间

海纳百川:HTTP的实体数据

从 HTTP 的 body 谈起。

数据类型与编码

​ MIME 把数据分成了八大类(MIME 是一个很大的标准规范,但HTTP知识取了其中的一部分)

  1. text,即文本格式的可读数据
  2. image,图像文件
  3. audio/video,音频和视频数据
  4. application,数据格式不固定,可能是文本也可能是二进制必须由上层应用程序来解释。常见的有
    application/json,application/javascript、application/pdf 等

因为 HTTP 在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续“”,还需要有一个“Encoding type”,告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据

  1. gzip,最流行的格式,nginx也一般用这个
  2. deflate,流行程度仅次于gzip
  3. br,一种专门为 HTTP 优化的新压缩算法

数据类型使用的头字段

Accept字段标记的是客户端可理解的 MIME type,可以用“,”做分隔符列出多个类型

语言类型与编码

需要明确区分的时候也要使用“typesubtype”的形式,不过这里的格式与数据类型不同分隔符不是“/”,而是“-”,例如:zh-CN 就表示我们最常使用的汉语。

语言类型使用的头字段

Accept-Language字段标记了客户端可理解的自然语言也允许用“,”做分隔符列出多个类型

响应报文里用头字段Content-Language告诉客户端实体数据使用的实际语言类型

字符集在 HTTP 里使用的请求头字段Accept-Charset,但响应头里却没有对应的 Content-Charset,而是在
Content-Type字段的数据类型后面用“charset=xxx”来表示

Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8

内容协商的质量值

Accept: text/html,application/xml;q=0.9,/;q=0.8

它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重
是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。

内容协商的结果

内容协商的过程是不透明的加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一
点信息,例如:Vary: Accept-Encoding,User-Agent,Accept

Vary 字段可以认为是响应报文的一个特殊的“版本标记“。也就是说,同一个 URI 可能会有多个不同
的“版本”主要用在传输链路中间的代理服务器实现缓存服务

Vary 字段,缓存代理必须要存储这些不同的版本。例如:,“Vary: Accept-Encoding”“Vary: User-Agent”,下次请求的时候就对比这些字段,看是否能完全匹配

小结

  1. 数据类型表示实体数据的内容是什么,使用的是 MIMEtype可以类比成你快递的物品),相关的头字段是 Accept(告诉服务器,客户端支持什么类型,以防服务器发过来的数据不认识,凡是有返回就可以带上) 和 Content-Type(请求和响应里都可以用,作用是指明body数据的类型,凡是有body数据都要带上);
  2. 数据编码表示实体数据的压缩方式,相关的头字段是Accept-EncodingContent-Encoding;(可以类比成物品的包装方式)
  3. 语言类型表示实体数据的自然语言,相关的头字段是Accept-Language 和 Content-Language
  4. 字符集表示实体数据的编码方式,相关的头字段是Accept-Charset 和 Content-Type
  5. 客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据;
  6. Accept 等头字段可以用“,”顺序列出多个可能的选项还可以用“;q=”参数来精确指定权重

把大象装进冰箱:HTTP传输大文件的方法

数据压缩

对处理视频,音频这些媒体数据的效果不佳。但是处理文本还是不错的。在Nginx 里就会使用“gzip on”指令,启用对“text/html”的压缩。

分块传输

‘这种“化整为零”的思路在 HTTP 协议里就是“chunked分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。

分块传输也可以用于“流式数据”(就是stream,它像从源头持续不断地,慢慢地”流“过来的,而不是一次性,一整块发过来的),对于未知长度的数据,还是挺好用的“Transfer-Encoding: chunked”和“Content-
Length”这两个字段是互斥的。这里补充一下:举个例子,从GitHub上下载源码包,GitHub要实时压缩实时发送,而不是一下子压缩好再发送,这样body的长度一开始就是未知的。所以就要用分块编码,压缩一部分,就发一部分,这部分的长度是已知的,但总长度只有压缩完才能知道chunked编码用在“流式”收发数据的时候,通常数据是即时生成的,也就是动态数据

范围请求

想获取一个大文件其中的片段数据(比如看电视剧的时候,跳过片头这个操作或者是拉进度条操作),而分块传输并没有这个能力。HTTP 协议为了满足这样的需求,提出了“范围请求”(range requests)的概念。允许客户端在请求头里使
专用字段
表示只获取文件的一部分,相当于是客户端的**“化整为零”。服务器必须在响应头**里使用字段“Accept-
Ranges: bytes”明确告知客户端:“我是支持范围请求的”

请求头Range是 HTTP 范围请求的专用字段格式是**“bytes=x-y”**,其中的 x 和 y 是以字节为单位的数据范围

服务器收到 Range 字段后,需要做四件事

1. 它必须检**查范围是否合法**,如果越界就返回416
2. 如果范围正确,服务器就可以根据 Range 头**计算偏移量**,**读取文件的片段了**,返回状态码“**206 Partial**
 **Content**”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
3. 服务器要添加一个响应头字段**Content-Range**,格式是**“bytes xy/length”**
4. 直接把片段用 TCP 发给客户端

实现的要点是:

  1. 先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
  2. 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
  3. 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了

多段数据

它还支持在Range 头里使用多个“x-y”一次性获取多个片段数据。这就需要使用MIME 类型:“multipart/byteranges”,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记

小结

  1. 压缩 HTML 等文本文件是传输大文件最基本的方法;
  2. 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块;
  3. 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206;这个“Content-Range”针对的是原文的数据,与压缩后的数据无关,因为我们在看视频的时候,拖拽进度条,拖拽的是原视频的长度,而不是压缩后的长度。
  4. 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,body 里的多个部分会用boundary 字符串分隔。

排队也要讲效率:HTTP的连接管理

短连接

因为客户端与服务器的整个连接过程很短暂不会与服务器保持长时间的连接状态

长连接

用的就是**“成本均摊”的思路,既然 TCP 的连接和关闭非常耗时间,那么就把这个时间成本由原来的一个“请求 - 应答”均摊到多个“请求 - 应答”上**。

连接相关的头字段

由于长连接对性能的改善效果非常显著,所以在 HTTP/1.1中的连接都会默认启用长连接“Connection: keepalive”
字段,服务器端通常不会主动关闭连接,但也可以使用一些策略。例如使用nginx的keepalive_timeout

队头阻塞

如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本。

性能优化

要解决队头阻塞,就要进行并发连接,缺陷就是:很容易造成连接数过多,而从被服务器认为是恶意攻击,反而造成”拒绝服务“

域名分片,还是用数量来解决质量的思路。多开几个域名,而这些域名都指向同一台服务器。

小结

  1. 早期的 HTTP 协议使用短连接,收到响应后就立即关闭连接,效率很低

  2. HTTP/1.1 默认启用长连接,在一个连接上收发多个请求响应,提高了传输效率;

  3. 服务器会发送**“Connection: keep-alive”字段表示启用了长连接**;

  4. 报文头里如果有**“Connection: close”就意味着长连接即将关闭**;

  5. 过多的长连接会占用服务器资源,所以服务器会用一些策略有选择地关闭长连接;

  6. **“队头阻塞”问题会导致性能下降,可以用“并发连接”和“域名分片”**技术缓解。

四通八达:HTTP的重定向和跳转

跳转动作是由浏览器的使用者主动发起的,可以称为“主动跳转

跳转是由服务器来发起的,称为“被动跳转

以上方法统一叫“重定向”(Redirection)

重定向的过程

Location”字段属于响应字段,必须出现在响应报文里。它标记了服务器要求重定向的 URI

注意,在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外,就必须用绝对 URI

重定向状态码

  1. 301俗称**“永久重定向”**意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI
  2. 302俗称“临时重定向
  3. 303 See Other,要求重定向后的请求改为GET 方法
  4. 307 Temporary Redirect:,重定向后请求里的方法和实体不允许变动
  5. 308 Permanent Redirect:,不允许重定向后的请求变动,但它是 301“永久重定向”的含义

重定向的应用场景

一个最常见的原因就是“资源不可用”,需要用另一个新的URI 来代替。另一个原因就是“避免重复

301:原来的 URI 已经不能用了(比如启用了新域名、服务器切换到了新机房、网站目录层次重构),必须用 301“永久重定向”

302:原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行

重定向的相关问题

第一个问题是“性能损耗”,重定向应当适度使用,决不能滥用。

第二个问题是“循环跳转”,可能会出现“A=>B=>C=>A”的无限循环

小结

  1. 重定向服务器发起的跳转要求客户端改用新的 URI重新发送请求,通常会自动进行,用户是无感知的;
  2. 301/302 是最常用的重定向状态码,分别是“永久重定向”和“临时重定向”;
  3. 响应头字段 Location 指示了要跳转的 URI,可以用绝对或相对的形式;
  4. 重定向可以把一个 URI 指向另一个 URI,也可以把多个URI 指向同一个 URI,用途很多;
  5. 使用重定向时需要当心性能损耗,还要避免出现循环跳转。

一句话,转发是服务器行为,重定向是客户端行为。具体解释可以看这篇文章https://blog.csdn.net/meiyalei/article/details/2129120


让我知道你是谁:HTTP的Cookie机制

什么是 Cookie?

相当于是服务器给每个客户端都贴上一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。

Cookie 的工作过程

这要用到两个字段:响应头字段Set-Cookie和请求头字段Cookie

Cookie 是由浏览器负责存储的,而不是操作系统。所以,它是“浏览器绑定”的,只能在本浏览器内生效

Cookie 的属性

首先,我们应该设置 Cookie 的生存周期,以使用 ExpiresMax-Age 两个属性来设置。“Expires”俗称“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。“Max-Age”用的是相对时间。Expires 和 Max-Age 可以同时出现,但浏览器会优先采用 Max-Age 计算失效期

其次,我们需要设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用

“Domain”和“Path”指定了 Cookie 所属的域名和路径

最后要考虑的就是Cookie 的安全性了,尽量不要让服务器以外的人看到。

1. **HttpOnly**,此 Cookie 只能通过浏览器 HTTP 协议传输
2. **SameSite**,可以防范“跨站请求伪造”(XSRF)攻击
3. **Secure**,表示这个 Cookie 仅能用 HTTPS 协议加密传输

这些信息,在application面板都能看到

Cookie 的应用

  1. 身份识别
  2. 广告跟踪

小结

  1. Cookie 是服务器委托浏览器存储的一些数据,让服务器有了“记忆能力”;
  2. 响应报文使用 Set-Cookie 字段发送“key=value”形式的 Cookie 值;
  3. 请求报文里用 Cookie 字段发送多个 Cookie 值;
  4. 为了保护 Cookie,还要给它设置有效期、作用域等属性,常用的有 Max-Age、Expires、Domain、HttpOnly 等;
  5. Cookie 最基本的用途是身份识别,实现有状态的会话事务。

生鲜速递:HTTP的缓存控制

实际上,HTTP 传输的每一个环节基本上都会有缓存,非常复杂。基于“请求 - 应答”模式的特点,可以大致分为客户端缓存和服务器端缓存,因为服务器端缓存经常与代理服务“混搭”在一起,所以今天我先讲客户端——也就是浏览器的缓存

服务器的缓存控制

Cache-Control里面的值“maxage=30”就是资源的有效时间,这里的 max-age 是响应报文的创建时刻

no_store不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
no_cache:它的字面含义容易与 no_store 搞混,实际的意思并不是不允许缓存,而是可以缓存,但在使用之前必须要去服务器验证是否过期,是否有最新的版本;(相当于吃之前必须问超市有没有更新鲜的,有就吃超市里的)
must-revalidate:又是一个和 no_cache 相似的词,它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。(相当于保鲜期内可以吃,过期了就要问超市让不让吃。)

客户端的缓存控制

“前进”“后退”“跳转”这些重定向动作中浏览器不会“夹带私货”,只用最基本的请求头,没有“Cache-Control”,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。只有用ctrl+F5刷新才会把Cache-Control”的max-age置为0,不用缓存

条件请求

ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。

比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。
再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。

小结

  1. 缓存是优化系统性能的重要手段,HTTP 传输的每一个环节中都可以有缓存
  2. 服务器使用**“Cache-Control”设置缓存策略**,常用的是“max-age”,表示资源的有效期;
  3. 浏览器收到数据就会存入缓存,如果没过期就可以直接使用,过期就要去服务器验证是否仍然可用;
  4. 验证资源是否失效需要使用“条件请求”,常用的是**“if-Modified-Since”和“If-None-Match”**,收到 304 就可以复用缓存里的资源;
  5. 验证资源是否被修改的条件有两个:“Last-modified”和“ETag”,需要服务器预先在响应报文里设置,搭配条件请求使用;
  6. 浏览器也可以发送“Cache-Control”字段,使用**“max-age=0”或“no_cache**”刷新数据。

良心中间商:HTTP的代理服务

代理服务

它就是在客户端和服务器原本的通信链路中插入的一个中间环节,也是一台服务器,但提供的是“代理服务”。

代理的作用

简单的说就是“欺上瞒下

代理最基本的一个功能是负载均衡

在负载均衡的同时,代理服务还可以执行更多的功能,比如:

健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;
安全防护:保护被代理的后端服务器,限制 IP 地址或流量,抵御网络攻击和过载;
加密卸载:对外网使用 SSL/TLS 加密通信认证,而在安全的内网不加密,消除加解密成本;
数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;
内容缓存:暂存、复用服务器响应

代理相关头字段

首先,代理服务器需要用字段**“Via”标明代理的身份**。

Via 是一个通用字段请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾,就像是经手人盖了一个章。

这个via就像微服务那个链路追踪,通过一个id识别整条链路的过程。Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息。

服务端为了可以知道客户端的真实IP地址,”,最常用的两个头字段是“X-Forwarded-For”和“X-Real-IP”。

X-Forwarded-For:追加的是请求方的 IP 地址(而via是没经过一个代理节点追加一个)

X-Real-IP:就是记录客户端 IP地址

代理协议

上面这些头字段存在几个问题:

  1. 通过这些头字段操作代理信息还必须要解析HTTP报文头,降低了转发的性能
  2. 要在原始头上修改原始报文,比如:在HTTPS通信中是不能被修改的

代理协议”(The PROXY protocol),它由知名的代理软件HAProxy 所定义,也是一个“事实标准”,被广泛采用(注意并不是 RFC)。

“代理协议”有 v1 和 v2 两个版本,v1 和 HTTP 差不多,也是明文,而 v2 是二进制格式。

v1:PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n

PROXY +ip地址类型+客户端ip+请求方ip+请求方端口+应答方端口+\r\n结束

服务器只需要解析第一行就能拿到客户端的ip了

小结

  1. HTTP 代理就是客户端和服务器通信链路中的一个中间环节为两端提供“代理服务”
  2. 代理处于中间层,为 HTTP 处理增加了更多的灵活性,可以实现负载均衡、安全防护、数据过滤等功能;
  3. 代理服务器需要使用字段“Via”标记自己的身份,多个代理会形成一个列表;
  4. 如果想要知道客户端的真实 IP 地址,可以使用字段**“X-Forwarded-For”和“X-Real-IP**”;
  5. 专门的“代理协议”可以在不改动原始报文的情况下传递客户端的真实 IP

补充:因为HTTP是明文传输的,请求头容易被篡改,所有**“X-Forwarded-For”**等字段不完全可信


冷链周转:HTTP的缓存代理(难点,为明白,要更多去结合nginx来学)

HTTP 的服务器缓存功能主要由代理服务器来实现(即缓存代理),而源服务器系统内部虽然也经常有各种缓存(如 Memcache、Redis、Varnish 等),但与 HTTP 没有太多关系,所以这里暂且不说。

缓存代理服务

简单来说,就是在代理服务器上加了缓存功能,让代理服务器既是客户端(要转发请求给服务端)又是服务器(可以处理请求,生成缓存),另一方面也可以说这个服务器既不是客户端也不是服务器,因为它只是数据的中转站,并不是真正的数据消费者和生产者

源服务器的缓存控制

private和public,private是表示缓存只能在客户端保存,对用户私有。public是缓存完全开放,谁都可以存,谁都可以用

must-revalidate,只要过期就必须回源服务器验证

proxy-revalidate,只要求代理的缓存过期后必须验证,客户端不必回源,只验证到代理这个环节就行了。

“s-maxage”,只限定在代理上能够存多久

“no-transform”,禁入进入缓存下来的数据做一些优化

源服务器在设置完“Cache-Control”后必须要为报文加上“Lastmodified”或“ETag”字段。否则,客户端和代理后面就无法使用条件请求来验证缓存是否有效,也就不会有 304 缓存重定向。

客户端的缓存控制

max-stale”的意思是如果代理上的缓存过期了也可以接受,但不能过期太多**,超过 x 秒也会不要**。“min-fresh”的意思是缓存必须有效而且必须在 x 秒后依然有效

比如,草莓上贴着标签“max-age=5”,现在已经在冰柜里存了 7 天。如果有请求“max-stale=2”,意思是过期两天也能接受,所以刚好能卖出去。
但要是“min-fresh=1”,这是绝对不允许过期的,就不会买走。这时如果有另外一个菠萝是“max-age=10”,那么“7+1<10”,在一天之后还是新鲜的,所以就能卖出去。

only-if-cached,表示只接受代理缓存的数据,不接受源服务器的响应。如果代理上没有缓存或者缓存过期,就应该给客户端返回一个504

Purge,缓存清理

小结

  1. 计算机领域里最常用的性能优化手段是“时空转换”,也就是“时间换空间”或者“空间换时间”,HTTP 缓存属于后者
  2. 缓存代理是增加了缓存功能的代理服务,缓存源服务器的数据,分发给下游的客户端;
  3. “**Cache-Control”**字段也可以控制缓存代理,常用的有“private”“smaxage”“no-transform”等,同样必须配合“Lastmodified”“ETag”等字段才能使用
  4. 缓存代理有时候也会带来负面影响,缓存不良数据,需要及时刷新或删除。

HTTPS是什么?SSL/TLS又是什么?

为什么要有 HTTPS?

简单的回答是“因为 HTTP 不安全”。

什么是安全?

通常认为,如果通信过程具备了四个特性,就可以认为是“安全”的,这四个特性是:机密性、完整性,身份认证和不可否认

  1. 机密性,不能让不相关的人看到不该看的东西
  2. 完整性,指数据在传输过程中没有被窜改
  3. 身份认证,也就是“证明你真的是你”
  4. 不可否认,不能“说话不算数”“耍赖皮”

什么是 HTTPS

新的协议名“https”,默认端口号 443,

由“HTTP over TCP/IP”变成了“HTTP over SSL/TLS

SSL/TLS

SSL 即安全套接层,在 1999 年把它改名为 TLS传输层安全,Transport Layer Security)

OpenSSL

它是一个著名的开源密码学程序库和工具包,几乎支持所有公开的加密算法和协议,已经成为了事实上的标准,许多应用软件都会使用它作为底层库来实现 TLS 功能,包括常用的 Web 服务器 Apache、Nginx 等

小结

  1. 因为 HTTP 是明文传输,所以不安全,容易被黑客窃听或窜改;
  2. 通信安全必须同时具备机密性、完整性,身份认证和不可否认这四个特性;
  3. HTTPS 的语法、语义仍然是 HTTP,但把下层的协议由 TCP/IP 换成了 SSL/TLS;
  4. SSL/TLS 是信息安全领域中的权威标准,采用多种先进的加密技术保证通信安全;
  5. OpenSSL 是著名的开源密码学工具包,是 SSL/TLS 的具体实现。

固若金汤的根本(上):对称加密与非对称加密

实现机密性最常用的手段是“加密”,就是把消息用某种方式转换成谁也看不懂(密文)的乱码,只有掌握特殊“钥匙”(密钥)的人才能再转换出原始文本。加密可以分为两大类:对称加密和非对称加密

对称加密

加密和解密时使用的密钥都是同一个,目前常用的只有 AES 和ChaCha20。

AES 的意思是“高级加密标准”(Advanced Encryption Standard),密钥长度可以是128、192 或 256。它是 DES 算法的替代者,安全强度很高,性能也很好,而且有的硬件还会做特殊优化,所以非常流行,是应用最广泛的对称加密算法。

ChaCha20 是 Google 设计的另一种加密算法,密钥长度固定为 256 位,纯软件运行性能要超过 AES,曾经在移动客户端上比较流行,但 ARMv8 之后也加入了 AES 硬件优化,所以现在不再具有明显的优势,但仍然算得上是一个不错算法。

加密分组模式

对称算法还有一个“分组模式”的概念,它可以让算法用固定长度的密钥加密任意长度的明文,把小秘密(即密钥)转化为大秘密(即密文)。

最新的分组模式被称为 AEAD(Authenticated Encryption withAssociated Data),在加密的同时增加了认证的功能,常用的是 GCM、CCM 和Poly1305。

非对称加密

其中有一个很大的问题:如何把密钥安全地传递给对方,术语叫“密钥交换

为了解决上述问题,所以,就出现了非对称加密(也叫公钥加密算法)

它有两个密钥,一个叫“公钥”(public key),一个叫“私钥”(private key)。

公钥和私钥有个特别的“单向”性

RSA 可能是其中最著名的一个,几乎可以说是非对称加密的代名词,它的安全性基于“整数分解”的数学难题,使用两个超大素数的乘积作为生成密钥的材料,想要从公钥推算出私钥是非常困难的。

ECC(Elliptic Curve Cryptography)是非对称加密里的“后起之秀”,它基于“椭圆曲线离散对数”的数学难题,使用特定的曲线方程和基点生成公钥和私钥,子算法 ECDHE 用于密钥交换,ECDSA 用于数字签名。

混合加密

在通信刚开始的时候使用非对称算法,比如 RSA、ECDHE,首先解决密钥交换的问题然后用随机数产生对称算法使用的“会话密钥”(session key),再用公钥加密

这个图的意思是:用非对称加密解决对称加密的密钥交换问题

小结

  1. 加密算法的核心思想是“把一个小秘密(密钥)转化为一个大秘密(密文消息)”,守住了小秘密,也就守住了大秘密;
  2. 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换,常用的有 AES 和 ChaCha20;
  3. 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢,常用的有 RSA 和 ECC;
  4. 把对称加密和非对称加密结合起来就得到了“又好又快”的混合加密,也就是 TLS 里使用的加密方式。

固若金汤的根本(下):数字签名与证书

机密性的基础上还必须加上完整性、身份认证等特性,才能实现真正的安全。

摘要算法

实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数

你可以把摘要算法近似地理解成一种特殊的压缩算法,它能够把任意长度的数据“压缩”成固定长度、而且独一无二的“摘要”字符串,就好像是给这段数据生成了一个数字“指纹”。

摘要算法理解成特殊的“单向”加密算法,它只有算法,没有密钥,加密后的数据无法解密不能从摘要逆推出原文

完整性

真正的完整性必须要建立在机密性之上,在混合加密系统里用会话密钥加密消息和摘要,这样黑客无法得知明文,也就没有办法动手脚了。(哈希消息认证码

数字签名

使用私钥再加上摘要算法,就能够实现“数字签名”,同时实现“身份认证”和“不可否认”

数字签名的原理其实很简单,就是把公钥私钥的用法反过来,之前是公钥加密、私钥解密,现在是私钥加密、公钥解密

数字证书和 CA

还有一个“公钥的信任”问题。因为谁都可以发布公钥,我们还缺少防止黑客伪造公钥的手段,也就是说,怎么来判断这个公钥就是你或者某宝的公钥呢

这时候可以用别的私钥(CA)给公钥签名。

CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。

CA 怎么证明自己呢?

证书体系的弱点

如果 CA 失误或者被欺骗,签发了错误的证书,虽然证书是真的,可它代表的网站却是假的。还有一种更危险的情况,CA 被黑客攻陷,或者 CA 有恶意,因为它(即根证书)是信任的源头,整个信任链里的所有证书也就都不可信了。

针对第一种,开发出了 CRL(证书吊销列表,Certificate revocation list)和 OCSP(在线证书状态协议,Online Certificate Status Protocol),及时废止有问题的证书。
对于第二种,因为涉及的证书太多,就只能操作系统或者浏览器从根上“下狠手”了,撤销对 CA 的信任,列入“黑名单”,这样它颁发的所有证书就都会被认为是不安全的。

小结

  1. 摘要算法用来实现完整性,能够为数据生成独一无二的“指纹”,常用的算法是 SHA-2;
  2. 数字签名是私钥对摘要的加密,可以由公钥解密后验证,实现身份认证和不可否认;
  3. 公钥的分发需要使用数字证书,必须由 CA 的信任链来验证,否则就是不可信的;
  4. 作为信任链的源头 CA 有时也会不可信,解决办法有 CRL、OCSP,还有终止信任。

信任始于握手:TLS1.2连接过程解析

HTTPS 建立连接

现在是 HTTPS 协议,它需要再用另外一个“握手”过程,这个“握手”过程与 TCP 有些类似,是 HTTPS 和 TLS 协议里最重要、最核心的部分,懂了它,你就可以自豪地说自己“掌握了 HTTPS”。

TLS 协议的组成

  1. 记录协议(Record Protocol)规定了 TLS 收发数据的基本单位:记录(record)。
  2. 警报协议,职责是向对方发出警报信息,有点像是 HTTP 协议里的状态码
  3. 握手协议,浏览器和服务器会在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。
  4. 变更密码规范协议,就是一个“通知”,告诉对方,后续的数据都将使用加密保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktBVN1C1-1589182906534)(C:%5CUsers%5Cprofessor%5CDesktop%5Cjava%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%5C26%E4%B8%A8%E4%BF%A1%E4%BB%BB%E5%A7%8B%E4%BA%8E%E6%8F%A1%E6%89%8B%EF%BC%9ATLS1.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG0qqW5b-1589182906535)(C:%5CUsers%5Cprofessor%5CDesktop%5Cjava%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%5C26%E4%B8%A8%E4%BF%A1%E4%BB%BB%E5%A7%8B%E4%BA%8E%E6%8F%A1%E6%89%8B%EF%BC%9ATLS11.jpg)]

RSA 握手过程

双向认证

上面说的是“单向认证”握手过程,建立了安全的连接之后,用账号和密码就能确认用户的身份

但为了防止账号、密码被盗,有的时候(比如网上银行)还会使用 U 盾给用户颁发客户端证书,实现“双向认证”,这样会更加安全。

小结

  1. HTTPS 协议会先与服务器执行 TCP 握手,然后执行 TLS 握手,才能建立安全连接;
  2. 握手的目标是安全地交换对称密钥,需要三个随机数,第三个随机数“Pre-Master”必须加密传输,绝对不能让黑客破解;
  3. “Hello”消息交换随机数,“Key Exchange”消息交换“Pre-Master”;
  4. “Change Cipher Spec”之前传输的都是明文,之后都是对称密钥加密的密文。

更好更快的握手:TLS1.3特性解析

TLS1.3 的三个主要改进目标:兼容、安全与性能。

小结

  1. 为了兼容 1.1、1.2 等“老”协议,TLS1.3 会“伪装”成 TLS1.2,新特性在“扩展”里实现;
  2. 1.1、1.2 在实践中发现了很多安全隐患,所以 TLS1.3 大幅度删减了加密算法,只保留了ECDHE、AES、ChaCha20、SHA-2 等极少数算法,强化了安全;
  3. TLS1.3 也简化了握手过程,完全握手只需要一个消息往返,提升了性能。

连接太慢该怎么办:HTTPS的优化

HTTPS 连接大致上可以划分为两个部分,第一个是建立 连接时的非对称加密握手,第二个是握手后的对称加密报文传输。

硬件优化

  1. 首先,你可以选择更快的 CPU
  2. 其次,你可以选择“SSL 加速卡”

软件优化

一个是软件升级,一个是协议优化

小结

  1. 可以有多种硬件和软件手段减少网络耗时和计算耗时,让 HTTPS 变得和 HTTP 一样快,最可行的是软件优化;
  2. 应当尽量使用 ECDHE 椭圆曲线密码套件,节约带宽和计算量,还能实现“FalseStart”;
  3. 服务器端应当开启“OCSP Stapling”功能,避免客户端访问 CA 去验证证书;
  4. 会话复用的效果类似 Cache,前提是客户端必须之前成功建立连接,后面就可以用“Session ID”“Session Ticket”等凭据跳过密钥交换、证书验证等步骤,直接开始加密通信。

我应该迁移到HTTPS吗

“迁移到 HTTPS”已经不是“要不要做”的问题,而是“要怎么做”的问题了

小结

  1. 从 HTTP 迁移到 HTTPS 是“大势所趋”,能做就应该尽早做;
  2. 升级 HTTPS 首先要申请数字证书,可以选择免费好用的“Let’s Encrypt”;
  3. 配置 HTTPS 时需要注意选择恰当的 TLS 版本和密码套件,强化安全;
  4. 原有的 HTTP 站点可以保留作为过渡,使用 301 重定向到 HTTPS

HTTP/2特性概览

头部压缩

HTTP/2 把“头部压缩”作为性能改进的一个重点,优化的方式你也肯定能想到,还是“压缩”。开发了专门的“HPACK”算法

二进制格式

虚拟的“流”

HTTP/2 为此定义了一个“流”(Stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流 ID。

用“”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。

在“”的层面上看,消息是一些有序的“帧”序列,而在“连接”的层面上看,消息却是乱序收发的“帧”(因为收发已经不是单纯的一问一答了)不会再出现“队头阻塞”问题,降低了延迟,大幅度提高了连接的利用率。

服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息

强化安全

为了区分“加密”和“明文”这两个不同的版本,HTTP/2 协议定义了两个字符串标识符:“h2”表示加密的 HTTP/2,“h2c”表示明文的 HTTP/2,多出的那个字母“c”的意思是“clear text”。

协议栈

小结

  1. HTTP 协议取消了小版本号,所以 HTTP/2 的正式名字不是 2.0;
  2. HTTP/2 在“语义”上兼容 HTTP/1,保留了请求方法、URI 等传统概念;
  3. HTTP/2 使用“HPACK”算法压缩头部信息,消除冗余数据节约带宽;
  4. HTTP/2 的消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”
  5. HTTP/2 使用虚拟的“”传输消息,解决了困扰多年的“队头阻塞”问题,同时实现了“多路复用”,提高连接的利用率;
  6. HTTP/2 也增强了安全性,要求至少是 TLS1.2,而且禁用了很多不安全的密码套件。

HTTP/2内核剖析

连接前言

TLS 握手成功之后,客户端必须要发送一个“连接前言”(connection preface),用来确认建立 HTTP/2 连接

头部压缩

“HPACK”算法是专门为压缩 HTTP 头部定制的算法,与 gzip、zlib 等压缩算法不同,它是一个**“有状态”的算法,需要客户端和服务器各自维护一份“索引表”,也可以说是“字典”(这有点类似 brotli),压缩和解压缩就是查表和更新表**的操作。

二进制帧

流与多路复用

  1. 流是可并发的,一个 HTTP/2 连接上可以同时发出多个流传输数据,也就是并发多请求,实现“多路复用”;
  2. 客户端和服务器都可以创建流,双方互不干扰
  3. 流是双向的,一个流里面客户端和服务器都可以发送或接收数据帧,也就是一个“请求-应答”来回;
  4. 流之间没有固定关系,彼此独立,但流内部的帧是有严格顺序的;
  5. 流可以设置优先级,让服务器优先处理,比如先传 HTML/CSS,后传图片,优化用户体验;
  6. 流 ID 不能重用,只能顺序递增,客户端发起的 ID 是奇数,服务器端发起的 ID 是偶数;
  7. 在流上发送“RST_STREAM”帧可以随时终止流,取消接收或发送;
  8. 第 0 号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。这里

流状态转换

小结

  1. HTTP/2 必须先发送一个“连接前言”字符串,然后才能建立正式连接
  2. HTTP/2 废除了起始行,统一使用头字段,在两端维护字段“Key-Value”的索引表,使用“HPACK”算法压缩头部;
  3. HTTP/2 把报文切分为多种类型的二进制帧,报头里最重要的字段是流标识符,标记帧属于哪个流;
  4. 流是 HTTP/2 虚拟的概念,是帧的双向传输序列,相当于 HTTP/1 里的一次“请求 -应答”;
  5. 在一个 HTTP/2 连接上可以并发多个流,也就是多个“请求 - 响应”报文,这就是“多路复用”。

补充:其实HTTP/2从本质上来看是队头阻塞的,因为http1里的请求和应答是没有序号标识的,导致了无法将乱序的请求和应答关联起来,也就是必须等待起始请求的应答先返回,则后续请求的应答都会延迟。而http2采用了虚拟的“”,每次请求应答都会分配同一个流id,而同一个流id里的又都是有序的,这样根据流id就可以标识出同一次的请求应答,不用再等待起始请求的应答先返回了,解决了“队头阻塞”。但是底层还是采用的TCP协议,该等的还是要等,只不过是同步非阻塞等而已(相当于linux的epoll模型)

htpp/2还是无状态,流状态只是表示流是否建立,单次请求响应的状态,并非会话级的状态保持(语法上有状态,语义上无状态)

每一个请求响应都是一个流,流和流之间可以并行,流内的帧还是有序串行。


未来之路:HTTP/3展望

它在 HTTP/2 的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题

QUIC 协议

它把下层的 TCP“抽掉”了,换成了 UDP。因为 UDP 是无序的,包之间没有依赖关系,所以就从根本上解决了“队头阻塞”。

QUIC 的特点

QUIC 基于 UDP,而 UDP 是“无连接”的,根本就不需要“握手”和“挥手”,所以天生就要比 TCP 快。

为了防止网络上的中间设备(Middle Box)识别协议的细节,QUIC 全面采用加密通信,可以很好地抵御窜改和“协议僵化”(ossification)。
###QUIC 内部细节
QUIC 的基本数据传输单位是包(packet)和帧(frame)一个包由多个帧组成面向的是“连接”,面向的是“”。

QUIC 使用不透明的“连接 ID”来标记通信的两个端点,客户端和服务器可以自行选择一组 ID 来标记自己,这样就解除了 TCP 里连接对“IP 地址 + 端口”(即常说的四元组)的强绑定,支持“连接迁移”(Connection Migration)。

比如你下班回家,手机会自动由 4G 切换到 WiFi。这时 IP 地址会发生变化,TCP 就必须重新建立连接。而 QUIC 连接里的两端连接 ID 不会变,所以连接在“逻辑上”没有中断,它就可以在新的 IP 地址上继续使用之前的连接,消除重连的成本,实现连接的无缝迁移。

小结

  1. HTTP/3 基于 QUIC 协议,**完全解决了“队头阻塞”问题,**弱网环境下的表现会优于HTTP/2;
  2. QUIC 是一个新的传输层协议,建立在 UDP 之上,实现了可靠传输;
  3. QUIC 内含了 TLS1.3,只能加密通信,支持 0-RTT 快速建连;
  4. QUIC 的连接使用“不透明”的连接 ID,不绑定在“IP 地址 + 端口”上,支持“连接迁移”
  5. QUIC 的流与 HTTP/2 的流很相似,但分为双向流和单向流;
  6. HTTP/3 没有指定默认端口号,需要用 HTTP/2 的扩展帧“Alt-Svc”来发现。

WAF:保护我们的网络服务

Web 服务遇到的威胁

  1. “DDoS”攻击,有时候也叫“洪水攻击”。黑客控制多台僵尸客户端,大量向服务器发起请求,让服务器无法提供正常服务
  2. SQL注入,它利用了服 务器字符串拼接形成 SQL 语句的漏洞,构造出非正常的 SQL 语句,获取数据库内部的敏感 信息。
  3. 跨站脚本”(XSS)攻击, 它属于“JS 代码注入”,利用 JavaScript 脚本获取未设防的 Cookie。

网络应用防火墙“WAF“

WAF 就是一种**“HTTP 入侵检测和防御系统”**。

通常一款产品能够称为 WAF,要具备下面的一些功能:
1. IP 黑名单和白名单,拒绝黑名单上地址的访问,或者只允许白名单上的用户访问;
2. URI 黑名单和白名单,与 IP 黑白名单类似,允许或禁止对某些 URI 的访问;
3. 防护 DDoS 攻击,对特定的 IP 地址限连限速;
4. 过滤请求报文,防御“代码注入”攻击
5. 过滤响应报文,防御敏感信息外泄;
6. 审计日志,记录所有检测到的入侵操作。

使用 WAF 最好“不要重新发明轮子”,而是使用现有的、比较成熟的、经过实际考 验的 WAF 产品。

全面的 WAF 解决方案

这里我就要“隆重”介绍一下 WAF 领域里的最顶级产品了:ModSecurity,它可以说是 WAF 界“事实上的标准”。

小结

  1. Web 服务通常都运行在公网上,容易受到“DDoS”、“代码注入”等各种黑客攻 击,影响正常的服务,所以必须要采取措施加以保护;
  2. WAF 是一种“HTTP 入侵检测和防御系统”,工作在七层,为 Web 服务提供全面的防 护;
  3. ModSecurity 是一个开源的、生产级的 WAF 产品,核心组成部分是“规则引 擎”和“规则集”,两者的关系有点像杀毒引擎和病毒特征库;
  4. WAF 实质上是模式匹配与数据过滤,所以会消耗 CPU,增加一些计算成本,降低服务 能力,使用时需要在安全与性能之间找到一个“平衡点”。

CDN:加速我们的网络服务

为什么要有网络加速

如果仅用现有的 HTTP 传输方式,大多数网站都会访问速度缓慢、用户体 验糟糕。

什么是 CDN?

它就是专门为解决“长距离”上网络访问速度慢而诞生的一种网络应用服务

CDN 有三个关键词:“内容”“分发”和“网络”。

网络:CDN 的最核心原则是“就近访问

内容:“内容”其实就是 HTTP 协议里的“资源”,比如超文本、图片、视频、 应用程序安装包等等**。“我们不生产内容,我们只是内容的搬运工。”**

CDN 的负载均衡

全局负载均衡和缓存系统,对应的是DNS和缓存代理技术

CDN 的缓存代理

这里就有两个 CDN 的关键概念:“命中”和“回源”

“命中”就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户;“回源”则正相反,缓存里没有,必须用代理的方式回源站取。

提高命中率:1. 增加硬件成本;2. 使用各种缓存中间件,如:redis;3. 分层(多级缓存),减少真正的回源


WebSocket:沙盒里的TCP

为什么要有 WebSocket

其实 WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式

这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏 等要求**“实时通信”**的领域。

WebSocket 的特点

WebSocket 是一个真正“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP“你拍一,我拍一”那么“客套”。

WebSocket 的帧结构

WebSocket 更侧重于**“实时通信”,而 HTTP/2 更侧重于提高传输效率**,所以两者的帧结构也有很大的区别。

WebSocket 的握手

WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字 段:Connection: Upgrade”,表示要求协议“升级”;“Upgrade: websocket”,表示要“升级”成 WebSocket 协议。

小结

  1. HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页 面,所以出现了 WebSocket;
  2. WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”, 让它运行在浏览器环境里;
  3. WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议 名“ws”和“wss”,端口号也沿用了 80 和 443;
  4. WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端 发数据必须掩码,服务器则不用;
  5. WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手 过程中有个非常简单的认证机制,目的是防止误连接。

总结

内容:“内容”其实就是 HTTP 协议里的“资源”,比如超文本、图片、视频、 应用程序安装包等等**。“我们不生产内容,我们只是内容的搬运工。”**

CDN 的负载均衡

全局负载均衡和缓存系统,对应的是DNS和缓存代理技术

CDN 的缓存代理

这里就有两个 CDN 的关键概念:“命中”和“回源”

“命中”就是指用户访问的资源恰好在缓存系统里,可以直接返回给用户;“回源”则正相反,缓存里没有,必须用代理的方式回源站取。

提高命中率:1. 增加硬件成本;2. 使用各种缓存中间件,如:redis;3. 分层(多级缓存),减少真正的回源


WebSocket:沙盒里的TCP

为什么要有 WebSocket

其实 WebSocket 与 HTTP/2 一样,都是为了解决 HTTP 某方面的缺陷而诞生的。HTTP/2 针对的是“队头阻塞”,而 WebSocket 针对的是“请求 - 应答”通信模式

这就导致 HTTP 难以应用在动态页面、即时消息、网络游戏 等要求**“实时通信”**的领域。

WebSocket 的特点

WebSocket 是一个真正“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不用像 HTTP“你拍一,我拍一”那么“客套”。

WebSocket 的帧结构

WebSocket 更侧重于**“实时通信”,而 HTTP/2 更侧重于提高传输效率**,所以两者的帧结构也有很大的区别。

WebSocket 的握手

WebSocket 的握手是一个标准的 HTTP GET 请求,但要带上两个协议升级的专用头字 段:Connection: Upgrade”,表示要求协议“升级”;“Upgrade: websocket”,表示要“升级”成 WebSocket 协议。

小结

  1. HTTP 的“请求 - 应答”模式不适合开发“实时通信”应用,效率低,难以实现动态页 面,所以出现了 WebSocket;
  2. WebSocket 是一个“全双工”的通信协议,相当于对 TCP 做了一层“薄薄的包装”, 让它运行在浏览器环境里;
  3. WebSocket 使用兼容 HTTP 的 URI 来发现服务,但定义了新的协议 名“ws”和“wss”,端口号也沿用了 80 和 443;
  4. WebSocket 使用二进制帧,结构比较简单,特殊的地方是有个“掩码”操作,客户端 发数据必须掩码,服务器则不用;
  5. WebSocket 利用 HTTP 协议实现连接握手,发送 GET 请求要求“协议升级”,握手 过程中有个非常简单的认证机制,目的是防止误连接。

总结

本次学习笔记来自于极客时间的http专栏,内容仅供个人复习用,如有写得不对的地方多多包涵!!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值