网络基础知识

TODO

HTTP/1.0 版本主要增加以下几点:

  • 增加了 HEAD、POST 等新方法。
  • 增加了响应状态码。
  • 引入了头部,即请求头和响应头。
  • 在请求中加入了 HTTP 版本号。
  • 引入了 Content-Type ,使得传输的数据不再限于文本。

HTTP/1.1 版本主要增加以下几点:

  • 新增了连接管理即 keepalive ,允许持久连接。
  • 支持 pipeline,无需等待前面的请求响应,即可发送第二次请求。
  • 允许响应数据分块(chunked),即响应的时候不标明Content-Length,客户端就无法断开连接,直到收到服务端的 EOF ,利于传输大文件。
  • 新增缓存的控制和管理。
  • 加入了 Host 头,用在你一台机子部署了多个主机,然后多个域名解析又是同一个 IP,此时加入了 Host 头就可以判断你到底是要访问哪个主机。

HTTP/2 版本主要增加以下几点:

  • 是二进制协议,不再是纯文本。
  • 支持一个 TCP 连接发起多请求,移除了 pipeline。
  • 利用 HPACK 压缩头部,减少数据传输量。
  • 允许服务端主动推送数据。
  • URI,统一资源标识符,作为互联网上的唯一标识。
  • HTML,超文本标记语言,描述超文本。
  • HTTP ,超文本传输协议,传输超文本。
与互联网的区别

互联网(Internet):是一个全球性的计算机网络基础设施,是硬件和通信技术的集合。

万维网(WWW):是互联网之上的一种服务,通过互联网来访问和共享信息资源。

简单来说,互联网是通道,而万维网是通过这些通道传输的信息资源。

在 1995 年开发的 Apache,简化了 HTTP 服务器的搭建,越来越多的人用上了互联网,促进了 HTTP 协议的修改。需求促使添加各种特性来满足用户的需求,经过了一系列的草案 HTTP/1.0 于 1996 年正式发布。

在 HTTP/1.0 版本主要增加以下几点:

  • 增加了 HEAD、POST 等新方法。
  • 增加了响应状态码。
  • 引入了头部,即请求头和响应头。
  • 在请求中加入了 HTTP 版本号。
  • 引入了 Content-Type ,使得传输的数据不再限于文本。

可以看到引入了新的方法,填充了操作的语义,像 HEAD 还可以只拿元信息不必传输全部内容,提高某些场景下的效率。

引入的响应状态码让请求方可以得知服务端的情况,可以区分请求出错的原因,不会一头雾水。

引入了头部,使得请求和响应更加的灵活,把控制数据和业务实体进行了拆分,也是一种解耦。

新增了版本号表明这是一种工程化的象征,说明走上了正途,毕竟没版本号无法管理。

引入了 Content-Type,支持传输不同类型的数据,丰富了协议的载体,充实了用户的眼球。

但是那时候 HTTP/1.0 还不是标准,没有实际的约束力。

HTTP/1.1 版本在 1997 的 RFC 2068 中首次被记录,从 1995 年至 1999 年间的第一次浏览器大战,极大的推动了 Web 的发展。随着发展 HTTP/1.0 演进成了 HTTP/1.1,并且在 1999 年废弃了之前的 RFC 2068,发布了 RFC 2616。

从版本号可以得知这是一个小版本的更新,更新主要是因为 HTTP/1.0 很大的性能问题,就是每请求一个资源都得新建一个 TCP 连接,而且只能串行请求。

所以在 HTTP/1.1 版本主要增加以下几点:

  • 新增了连接管理即 keepalive ,允许持久连接。
  • 支持 pipeline,无需等待前面的请求响应,即可发送第二次请求。
  • 允许响应数据分块(chunked),即响应的时候不标明Content-Length,客户端就无法断开连接,直到收到服务端的 EOF ,利于传输大文件。
  • 新增缓存的控制和管理。
  • 加入了 Host 头。比如一台机子部署了多个主机,然后多个域名解析又是同一个 IP,此时加入了 Host 头就可以判断到底是要访问哪个主机。

HTTP/1.1 ,主要是为了解决性能的问题,包括支持持久连接、pipeline、缓存管理等等,也添加了一些特性。

在 2014 年对 HTTP/1.1 又做了一次修订,这时候 HTTP/1.1 已经成了标准。

随着 HTTP/1.1 的发布,互联网也开始了爆发式的增长,这种增长暴露出 HTTP 的不足,主要还是性能问题。在 2015 年发布了 HTTP/2。

HTTP/2 版本主要增加以下几点:

  • 是二进制协议,不再是纯文本。
  • 支持一个 TCP 连接发起多请求,移除了 pipeline。
  • 利用 HPACK 压缩头部,减少数据传输量。
  • 允许服务端主动推送数据。

从文本到二进制其实简化了整齐的复杂性,解析数据的开销更小,数据更加紧凑,减少了网络的延迟,提升了整体的吞吐量。

支持一个 TCP 连接发起多请求,即支持多路复用,像 HTTP/1.1 pipeline 还是有阻塞的情况,需要等前面的一个响应返回了后面的才能返回

而多路复用就是完全异步化,这减少了整体的往返时间(RTT),解决了 HTTP 队头阻塞问题,也规避了 TCP 慢启动带来的影响

HPACK 压缩头部,采用了静态表、动态表和哈夫曼编码,在客户端和服务器都维护请求头的列表,所以只需要增量和压缩过的头部信息,服务端拿到之后组装一下就能得到完整的头部信息。

如下图:

服务端主动推送数据,这个其实就是减少了请求的次数,比如客户端请求 1.html,我把 1.html 需要的 js 和 css 也一块送过去,省的之后客户端再请求我要 js ,我要这个 css。

HTTP/2 的整体演进都是往性能优化的角度发展,因为此时的性能是痛点。但是 HTTP/1.1 这个历史包袱太重了,所以 HTTP/2 到现在也只有大致一半的网站使用它。

HTTP 的痛点来自于 HTTP 依赖的 TCP。

TCP 是面向可靠的、有序的传输协议,因此会有失败重传和按序机制,而 HTTP/2 是所有流共享一个 TCP 连接,所以会有 TCP 层面的队头阻塞,当发生重传时会影响多个请求响应。

并且 TCP 是基于四元组(源IP,源端口,目标IP,目标端口)来确定连接的,而在移动网络的情况下 IP 地址会频繁的换,这会导致反复的建连。TCP 与 TLS 的叠加握手,增加了延时。

问题就出在 TCP 身上,这时 Google 就把目光瞄向了 UDP。

UDP 是无连接的,不管什么顺序,也不管什么丢包,而 TCP 太保守了,因此 Google 就研究出了 QUIC 协议。

QUIC 层来实现自己的丢包重传和拥塞控制,出于安全的考虑现在会使用 HTTPS ,所以需要多次握手。

在移动互联网时代握手的消耗会放大,于是 QUIC 引入了个叫 Connection ID 来标识一个链接,切换网络之后可以复用这个连接,达到 0 RTT 就能开始传输。

上图是在已经和服务端握过手之后的,由于网络切换等原因才有 0 RTT ,也就是 Connection ID 在之前生成过了

如果是第一次建连还是需要多次握手的,可以来看一下简化的握手对比图。

所以所谓的 0RTT 是在之前已经建连的情况下。

还有 HTTP/2 提到的 HPACK,这个是依赖 TCP 的可靠、有序传输的,于是 QUIC 得搞了个 QPACK,也采用了静态表、动态表和哈夫曼编码。它丰富了 HTTP/2 的静态表,从 61 项加到了 98 项。

这个动态表,是用来存储未包含在静态表中的头部项,假设动态表还未收到,后面来解头部的时候肯定要被阻塞的。

所以 QPACK 就另开一条路,在单向的 Stream 里传输动态表的编解码,单向传输好了,接受端到才能开始解码,也就是说还没好你就先别管,防止做一半卡住了

对于 TCP 队头阻塞,因为 TCP 不认识每个流分别是哪个请求的,所以它只能全部阻塞住,而 QUIC 知道,因此比如请求 A 丢包了,我就把 A 卡住了就行,请求 B 完全可以全部放行,丝毫不受影响。

可以看到基于 UDP 的 QUIC 还是很强大的,在 2018 年,互联网标准化组织 IETF 提议将 HTTP over QUIC 更名为 HTTP/3 并获得批准

根据 RFC793 定义,TCP 的连接就是:TCP 为每个数据流初始化并维护的某些状态信息(这些信息包括 socket、序列号和窗口大小),称为连接。

面向连接的并不是真的是拉了一条线让端与端之间连起来,只是双方都维护了一个状态,通过每一次通信来维护状态的变更,使得看起来好像有一条线关联了对方。

  1. Socket:

在 TCP/IP 协议中,Socket 是通信的端点。由 IP 地址和端口号组成,如 192.168.1.1:8084。在编程中,Socket 是用于网络通信的接口,通过它,应用程序可以发送和接收数据。

  1. 序列号 (Sequence Number):

TCP 序列号在传输过程中非常关键,因为它保证了数据传输的有序性和完整性。在三次握手中,双方交换初始序列号 (ISN),并在此基础上为后续的每个数据段分配序列号。

序列号有助于接收方按顺序重组数据包,并检测丢包情况。

  1. 窗口大小 (Window Size):

TCP 窗口大小指的是在特定时刻,接收方能够接收的最大数据量。这个大小由接收方通知发送方,表明接收方的缓冲区能处理多少数据。

它直接影响 TCP 的流量控制和拥塞控制机制。通过调整窗口大小,TCP 可以避免发送过多数据导致接收方的缓冲区溢出,也能根据网络状况调整发送速率。

  1. 三元组 (3-tuple):

三元组指的是 IP 地址和端口号的组合,即 IP 地址 + 端口号 + 协议类型。例如,192.168.1.1:8084 (TCP) 就是一个三元组。在一个机器上,这样的组合唯一标识了一个网络服务或应用程序。

  1. 四元组 (4-tuple):

四元组即 源 IP 地址 + 源端口号 + 目的 IP 地址 + 目的端口号这四个要素唯一标识了一个 TCP 连接。

例如,一个客户端通过 IP 地址 192.168.1.100 和端口 50000 连接到服务器 192.168.1.1 的端口 80,则这个连接可以表示为 192.168.1.100:50000 -> 192.168.1.1:80

这就是一个四元组,唯一标识了该连接。

  1. 数据传输安全性:
  • HTTP:数据以明文传输,容易被窃听、篡改。
  • HTTPS:通过 SSL/TLS 协议对数据进行加密传输,提供数据机密性和完整性保障。
  1. 端口号:
  • HTTP:默认使用端口 80。
  • HTTPS:默认使用端口 443。
  1. 性能:
  • HTTP:无加密过程,连接建立速度稍快。
  • HTTPS:基于 HTTP上又加了 SSL(Secure Sockets Layer)或 TLS(Transport Layer Security) 协议来实现的加密传输,加解密过程增加了计算开销,握手时间较长,但现代硬件和协议优化已使性能差距减小。
  1. SEO 影响:
  • HTTP:搜索引擎一般会降低未加密站点的排名。
  • HTTPS:搜索引擎更倾向于优先展示 HTTPS 网站。

HTTPS 使用 TLS 协议进行握手,TLS 的握手根据密钥交互算法的不同,可以分为两种,一种是 RSA 算法另一种是 ECDHE 算法。

一共需要四次握手:

  • 客户端问候(ClientHello)
  • 服务器问候(ServerHello)
  • 客户端密钥交换(Client Key Exchange) + 开始使用加密(Change Cipher Spec) + 客户端完成(Client Finished)
  • 服务器发送开始使用加密(Change Cipher Spec) + 服务器完成(Server Finished)
  1. 客户端问候(ClientHello)

客户端向服务器发送一个 ClientHello 消息,包含:

  • TLS 版本
  • 加密算法套件(Cipher Suites)
  • 随机数。
  1. 服务器问候(ServerHello)

服务器接收到 ClientHello 后,会认证 TLS 版本号是否支持,选择一个加密算法套件,保证客户端的随机数,再生成一个随机数。所以 ServerHello 的消息包含:

  • 确认的 TLS 版本
  • 确认的加密算法套件(Cipher Suite)
  • 随机数。

除此之外,服务器还需向客户端发送自己的数字证书,内含公钥,用于证明其身份,这个步骤是服务器证书(Server Certificate)

最后,服务器会发送服务器完成(ServerHelloDone),表示握手的初步阶段结束。

  1. 客户端密钥交换(Client Key Exchange) + 开始使用加密(Change Cipher Spec)+ 客户端完成(Client Finished)

客户端通过 CA(证书认证机构)验证服务端传递过来的服务器证书可信后,再次生成一个随机数(pre-master),通过证书得到的公钥,加密通过客户端密钥交换(Client Key Exchange)发送给服务端。

紧接着再发送开始使用加密(Change Cipher Spec)给服务器端。因为此时不论是客户端还是服务端都拿到了三个随机数(第一次客户端给的、第二次服务端给的、第三次客户端的 pre-master)。

因此这三个随机数就可以作为对称加密的密钥,用户后续传输的加解密。这个步骤后,后续的传输数据都是加密的。

发送完(Change Cipher Spec)后,客户端再发送客户端完成(Client Finished),这个 Finished 会带上 Encrypted Handshake Message,这个 message 就是之前发送的所有数据的摘要,并且还用生成的对称加密密钥加密了,传递给服务器端验证,预防握手过程中的握手信息被修改。

  1. **服务器发送开始使用加密(Change Cipher Spec) + 服务器完成(Server Finished)

同理,服务器也是一样发送(Change Cipher Spec),代表后续要用加密数据传输了,且发送握手摘要 给客户端验证,一切正常的话, RSA TLS 握手就结束了。

验证

同样也需要四次握手,大致步骤和 RSA 是一致的,主要区别在第二步:

  1. 客户端问候(ClientHello)
  2. 服务器问候(ServerHello)+ 服务器密钥交换(Client Key Exchange)
  3. 客户端密钥交换(Client Key Exchange) + 开始使用加密(Change Cipher Spec) + 客户端完成(Client Finished)
  4. 服务器发送开始使用加密(Change Cipher Spec) + 服务器完成(Server Finished)

但是内部细节交互和 RSA 有一些不同

  1. 客户端问候(ClientHello)

客户端向服务器发送一个 ClientHello 消息,包含:

  • TLS 版本
  • 加密算法套件(Cipher Suites)
  • 随机数。

这步和 RSA 一致。

  1. 服务器问候(ServerHello)+ 服务器密钥交换(Client Key Exchange)

服务器接收到 ClientHello 后,会认证 TLS 版本号是否支持,选择一个加密算法套件,保证客户端的随机数,再生成一个随机数。所以 ServerHello 的消息包含:

  • 确认的 TLS 版本
  • 确认的加密算法套件(Cipher Suite),这里会选 ECDHE 相关的套件
  • 随机数。

同样,服务器也会向客户端发送自己的数字证书,内含公钥,用于证明其身份,这个步骤是服务器证书(Server Certificate),主要区别是:

ECDHE 实际上是基于椭圆曲线特性的算法,此时服务器需要确认选择的椭圆曲线以及一个随机数作为服务器椭圆曲线私钥,基于椭圆曲线和私钥算出椭圆曲线公钥。

为了防止公钥被修改,服务器通过 RSA 给公钥签名,最终利用(Server Key Exchange)发送给客户端。

Server Key Exchange 消息的内容,包含:

  • 椭圆曲线(如果客户端未指定)。
  • 服务器的椭圆曲线公钥(用于 Diffie-Hellman 密钥交换)。
  • 签名:服务器使用其私钥对相关参数进行签名(包括椭圆曲线参数和公钥),以确保这些参数没有被篡改。

最后,服务器会发送服务器完成(ServerHelloDone),表示握手的初步阶段结束。

  1. 客户端密钥交换(Client Key Exchange) + 开始使用加密(Change Cipher Spec)+ 客户端完成(Client Finished)

同样,客户端通过 CA(证书认证机构)验证服务端传递过来的服务器证书可信后,生成一个随机数作为客户端椭圆曲线私钥,基于椭圆曲线和私钥算出椭圆曲线公钥。

将这个公钥通过客户端密钥交换(Client Key Exchange)发送给服务端。

此时客户端和服务端拥有【客户端随机数+服务端随机数+共享密钥(对方公钥+自己私钥计算得到)】,这三个元素生成最终的会话密钥。

紧接着再发送开始使用加密(Change Cipher Spec)给服务器端。这个步骤后,后续的传输数据都是加密的。

发送完(Change Cipher Spec)后,客户端再发送客户端完成(Client Finished),这个 Finished 会带上 Encrypted Handshake Message,这个 message 就是之前发送的所有数据的摘要,并且还用生成的对称加密密钥加密了,传递给服务器端验证,预防握手过程中的握手信息被修改。

  1. 服务器发送开始使用加密(Change Cipher Spec)+ 服务器完成(Server Finished)

同理,服务器也是一样发送(Change Cipher Spec),代表后续要用加密数据传输了,且发送握手摘要 给客户端验证,一切正常的话, ECDHE TLS 握手就结束了。

所以 ECDHE 和 RSA 主要差别在第二次握手,服务端需要发送 (Server Key Exchange)。

可以从三个角度来看:

  • 安全性:ECDHE 提供前向安全性,而 RSA 不具备。如果服务器的私钥泄露,基于 ECDHE 的握手不会影响之前的会话,而基于 RSA 的握手会导致之前的通信被解密。
  • 计算复杂度:ECDHE 由于涉及椭圆曲线数学运算,相比 RSA 的操作更复杂,但提供更高的安全性。
  • 使用场景:现代 HTTPS 实践中,ECDHE 已成为首选,因为它能够提供前向安全性,同时结合 RSA 或 ECDSA 用于签名和认证。

还有一点,基于 RSA 的 TLS 需要等四次握手完全结束后,客户端才可以发送数据,而 ECDHE 在客户端得到完整密钥后,可以直接开始发送数据。

HTTPS 使用的加密协议从最初的 SSL(Secure Sockets Layer)演变为 TLS(Transport Layer Security)。

目前广泛使用的是 TLS 1.2 和 TLS 1.3。TLS 1.3 引入了更快的握手机制(0-RTT),进一步降低了延迟(QUIC内嵌使用这个协议)。