JS核心理论之《HTTP、Websocket网络协议》

1,559 阅读11分钟

基础概念

OSI 是 Open System Interconnect的 缩写,意为开放式系统互联。 TCP/IP模型可以看做是对OSI模型的一种简化。

WechatIMG354 (1)

  • OSI模型从底层到上层依次是:物理层->链路层->网络层->传输层->会话层->表示层->应用层
  • TCP/IP模型从底层到上层依次是:物理链路层->网络层->传输层->应用层

HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的。我们可以把这些高级协议理解成对 TCP 的封装。 既然大家都使用 TCP 协议,那么大家的连接和断开,都要遵循 TCP 协议中的三次握手四次挥手 ,只是在连接之后发送的内容不同,或者是断开的时间不同。

握手原理

image

  • SYN:建立连接标志位
  • ACK:确认标志位
  • FIN:结束标志位
  • Seq:序列化编号
  • Ack number:确认编号

三次握手

  • 第一次握手:建立连接时,客户端A发送SYN包(SYN置为1,seq=x)到服务器B,并进入SYN_SEND状态,等待服务器B确认;
  • 第二次握手:服务器收到SYN报文段,需要对这个SYN报文段进行确认,设置Ack number为seq+1(即 Ack置为1,Ack number = x+1); 同时,自己还要发送SYN请求信息,将SYN置为1,seq=y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
  • 第三次握手:客户端收到服务器的SYN+ACK报文段,然后将Ack number设置为y+1,即(SYN位为0,ACK位为1,Ack number = y+1),向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

四次挥手

  • 第一次挥手:主机1(可以使客户端,也可以是服务器端),设置Seq 和Ack Number,向主机2发送一个FIN报文段(FIN位为1,seq = x+2, Ack number = y+1);此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
  • 第二次挥手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Ack number为Seq加1,即Ack number = x+3;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我“同意”你的关闭请求;
  • 第三次挥手:主机2向主机1发送FIN报文段(FIN置为1,seq=y+1),请求关闭连接,同时主机2进入LAST_ACK状态;
  • 第四次挥手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段(Ack number = y+2),然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接; 此时,主机1等待2MSL(最大报文生存时间)后依然没有收到回复,则证明主机2已正常关闭,那好,主机1也可以关闭连接了。

HTTP协议

HTTP协议:超文本传输​​协议,是用于传输诸如HTML的超媒体文档的应用层协议。影响一个 HTTP 网络请求的因素主要有两个:带宽和延迟带宽可以通过提升网络基础设施来优化,目前已没有大的障碍。影响HTTP的就只剩下延迟了。

延迟的因素主要有三个方面:

  • 建立连接:HTTP建立连接,无法复用会导致每次请求都经历三次握手慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。
  • HTTP队头阻塞:Head of line blocking,浏览器会因为一些原因阻塞请求。比如浏览器对于同一个域名的并行连接的总数限制为少量几个,(通常是4个,随浏览器内核不同而不同),超过浏览器最大连接数限制,后续请求就会被阻塞。
  • DNS查询:浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS。这个通常可以利用DNS缓存结果来达到减少这个时间的目的。

提到队头阻塞,指的可能是TCP协议中的队头阻塞,但是HTTP/1.1中也有一个类似TCP队头阻塞的问题。

  • TCP队头阻塞(head-of-line blocking)发生在一个TCP分节丢失,导致其后续分节不按顺序到达接收端。该后续分节将被接收端一直保持,直到丢失的第一个分节被发送端重传并到达接收端为止。
  • HTTP队头阻塞,是由于HTTP/1.1管道化本身可能会导致队头阻塞的问题。因此现代浏览器默认都关闭了管道化。 因此可见队头阻塞的原因其实是因为消息传递的过程是基于一个链路,也就是一个队列,而队列是要遵循严格的顺序,比如TCP要求数据严格按照序号顺序,HTTP管道化要求响应严格按照请求顺序返回。

HTTP/1.0

HTTP/1.0是第一个在通讯中指定版本号的HTTP协议版本,至今仍被广泛采用,特别是在代理服务器中。HTTP/1.0最大的问题的就是连接无法复用

HTTP/1.1

HTTP/1.1是当前版本,持久连接被默认采用,并能很好地配合代理服务器工作。通过以管道pipelining方式一次性发送多个请求,以便降低线路负载,提高传输速度。 然而这种技术在接收响应时,要求必须按照发送请求的顺序返回。如果第一个请求被堵塞了,则后面的请求即使处理完毕了,也需要等待。 这就是HTTP/1.1由于管道pipelining技术导致的队头阻塞问题。

HTTP/2.0

为了解决上述队头阻塞问题,HTTP/2.0中提出了 多路复用(Multiplexing) 技术,将多个请求复用同一个tcp链接中,将一个TCP连接分为若干个流(Stream),每个流中可以传输若干消息(Message), 每个消息由若干最小的二进制分帧(Frame)组成。也就是将每个请求-响应拆分为了细小的二进制帧Frame,这样即使一个请求被阻塞了,也不会影响其他请求。

HTTP2.0多路复用与HTTP1.1长连接复用区别

  • HTTP/1.1管道Pipeling将若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的出现队头阻塞;
  • HTTP/2多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行。

image

除了多路复用二进制分帧传输以外,HTTP/2.0还提供了首部压缩服务端推送技术。

  • HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
  • 服务端推送是一种在客户端请求之前发送数据的机制。在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应。

SPDY

注意:其实在HTTP/2.0出现之前,有一个SPDY方案,是2012年google针对HTTP1.X的请求延迟和安全性提出的,而HTTP/2.0是基于SPDY设计的。但二者又有一些不同。

  • HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS。
  • HTTP2.0 消息头的压缩算法采用 HPACK,而 SPDY 采用的 DEFLATE。

QUIC

谷歌提出的QUIC(Quick UDP Internet Connections),则是基于UDP + HTTP/2.0的一个实验性的快速传输协议,UDP是面向数据报文的,所以遇到丢包的情况也不会进行重传,从而进一步减少网络延迟、解决队头阻塞问题。

总结:

  • HTTP/2 over TCP(我们接触最多的HTTP/2)解决了HTTP级别的队头阻塞问题。
  • HTTP/2 over QUIC(UDP)解决了传输层的队头阻塞问题,即TCP队头阻塞(除去header frame),是我们理解的真正解决了该问题。

HTTP/3.0

虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2 的缺点主要有以下几点:

  • TCP 和 TCP+TLS 建立连接的延时 HTTP/2 是使用 TCP 协议来传输的。如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程:
  1. 在建立 TCP 连接的时候,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完 1.5 个 RTT 之后才能进行数据传输。
  2. 进行 TLS 连接,TLS 有两个版本——TLS1.2 和 TLS1.3,每个版本建立连接所花的时间不同,大致是需要 1~2 个 RTT。 总之,在传输数据之前,我们需要花掉 3~4 个 RTT。
  • TCP 的队头阻塞并没有彻底解决 HTTP/2.0 中,多个请求是跑在一个 TCP 管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。 因为 TCP 为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2 出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP 连接中的所有请求。 而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反倒只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

让 HTTP 跑在 QUIC 上而不是 TCP 上。而这个HTTP over QUIC就是 HTTP 协议的下一个大版本,HTTP/3

image

总结:

  • HTTP/1.1 有两个主要的缺点:安全不足和性能不高。
  • HTTP/2 完全兼容 HTTP/1,是“更安全的 HTTP、更快的 HTTPS",头部压缩、多路复用等技术可以充分利用带宽,降低延迟,从而大幅度提高上网体验。
  • QUIC 基于 UDP 实现,是 HTTP/3 中的底层支撑协议。该协议基于 UDP,又汲取了 TCP 中的精华,实现了既快又可靠的协议。

HTTPS

HTTP与HTTPS的区别就在加密。tls是ssl3.0以后的版本,可以理解为ssl3.1。

HTTP Strict Transport Security (通常简称为HSTS) 是一个安全功能,它告诉浏览器只能通过HTTPS访问当前资源, 禁止HTTP方式。

image
image

Websocket

Websocket是html5提出的一个协议规范,是为解决客户端与服务端实时通信。 本质上是一个基于TCP,先通过HTTP/HTTPS协议发起一条特殊的HTTP请求进行握手后,创建一个用于交换数据的TCP连接。

建立连接过程

首先客户端向服务端发起一个特殊的 HTTP 请求,其消息头如下:

GET /chat HTTP/1.1  // 请求行
Host: server.example.com
Upgrade: websocket  // required
Connection: Upgrade // required
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // required
Origin: http://example.com  // 用于防止未认证的跨域脚本使用浏览器 websocket api 与服务端进行通信
Sec-WebSocket-Protocol: chat, superchat  // optional, 子协议协商字段
Sec-WebSocket-Version: 13

如果服务端支持该版本的 WebSocket,会返回 101 响应,响应标头如下:

HTTP/1.1 101 Switching Protocols  // 状态行
Upgrade: websocket   // required
Connection: Upgrade  // required
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密后的 Sec-WebSocket-Key
Sec-WebSocket-Protocol: chat // 表明选择的子协议

握手完成后,接下来的 TCP 数据包就都是 WebSocket 协议的帧了。

可以看到,这里的握手不是 TCP 的握手,而是在 TCP 连接内部,从 HTTP/1.1 upgrade 到 WebSocket 的握手。

与HTTP长连接区别

  • HTTP/1.1通过使用keep-alive进行长连接,HTTP/1.1默认进行持久连接。 在一次 TCP 连接中可以完成多个 HTTP 请求,但是对每个请求仍然要单独发 headerKeep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器中设定这个时间。这种长连接是一种“伪链接”。 双方并没有建立正真的连接会话,服务端可以在任何一次请求完成后关闭。
  • Websocket的长连接,是一个真正的全双工。长连接第一次TCP链路建立之后,后续数据可以双方都进行发送,不需要发送请求头,两边都必须要维持住连接的状态。