网络协议很基础,基础到平时开发中,你说用到吧也能用到,你说用不到也是,基本上只要接口能通,后端能返回数据前端基本就完事了,但作为一枚合格的前端开发工程师,了解基本的网络通信大有裨益

http

什么是 HTTP 队头阻塞?

从前面的小节可以知道,HTTP 传输是基于请求-应答的模式进行的,报文必须是一发一收,但值得注意的是,里面的任务被放在一个任务队列中串行执行,一旦队首的请求处理太慢,就会阻塞后面请求的处理。这就是著名的HTTP队头阻塞问题。

HTTP1.1 如何解决 HTTP 的队头阻塞问题?

  • 并发连接
    对于一个域名允许分配多个长连接,那么相当于增加了任务队列,不至于一个队伍的任务阻塞其它所有任务。在RFC2616规定过客户端最多并发 2 个连接,不过事实上在现在的浏览器标准中,这个上限要多很多,Chrome 中是 6 个。但其实,即使是提高了并发连接,还是不能满足人们对性能的需求。
  • 域名分片
    一个域名不是可以并发 6 个长连接吗?那我就多分几个域名。
    比如 content1.sanyuan.com 、content2.sanyuan.com。
    这样一个sanyuan.com域名下可以分出非常多的二级域名,而它们都指向同样的一台服务器,能够并发的长连接数更多了,事实上也更好地解决了队头阻塞的问题。

HTTP/1.1的缺陷

1.高延迟–带来页面加载速度的降低
网络延迟问题主要由于队头阻塞(Head-Of-Line Blocking),导致带宽无法被充分利用。
队头阻塞是指当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。针对队头阻塞,人们尝试过以下办法来解决:

  • 域名分片 将同一页面的资源分散到不同域名下,提升连接上限。 Chrome有个机制,对于同一个域名,默认允许同时建立 6 个 TCP持久连接,使用持久连接时,虽然能公用一个TCP管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。另外如果在同一个域名下同时有10个请求发生,那么其中4个请求会进入排队等待状态,直至进行中的请求完成。
  • 雪碧图 Spriting合并多张小图为一张大图,再用JavaScript或者CSS将小图重新“切割”出来的技术。
  • 内联(Inlining)是另外一种防止发送很多小图请求的技巧,将图片的原始数据嵌入在CSS文件里面的URL里,减少网络请求次数。
  • 拼接(Concatenation)将多个体积较小的JavaScript使用webpack等工具打包成1个体积更大的JavaScript文件,但如果其中1个文件的改动就会导致大量数据被重新下载多个文件。

2.无状态特性–带来的巨大HTTP头部
header里携带的内容过大,在一定程度上增加了传输的成本。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费。

3.明文传输–带来的不安全性
4.不支持服务端推送

HTTP/2 新特性

二进制传输

HTTP/2传输数据量的大幅减少,主要有两个原因:以二进制方式传输和Header 压缩。我们先来介绍二进制传输,HTTP/2 采用二进制格式传输数据,而非HTTP/1.x 里纯文本形式的报文 ,二进制协议解析起来更高效。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。

Header 压缩

HTTP/2并没有使用传统的压缩算法,而是开发了专门的”HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。

多路复用

在 HTTP/2 中引入了多路复用的技术。多路复用很好的解决了浏览器限制同一个域名下的请求数量的问题,同时也接更容易实现全速传输,毕竟新开一个 TCP 连接都需要慢慢提升传输速度。

在 HTTP/2 中,有了二进制分帧之后,HTTP /2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2中,

  • 同域名下所有通信都在单个连接上完成。
  • 单个连接可以承载任意数量的双向数据流。
  • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。

这一特性,使性能有了极大提升:

  • 同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应,这样整个页面资源的下载过程只需要一次慢启动,同时也避免了多个TCP连接竞争带宽所带来的问题。
  • 并行交错地发送多个请求/响应,请求/响应之间互不影响。
  • 在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。

Server Push

HTTP2还在一定程度上改变了传统的“请求-应答”工作模式,服务器不再是完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文件发给客户端,减少等待的延迟,这被称为”服务器推送”( Server Push,也叫 Cache push)

提高安全性

出于兼容的考虑,HTTP/2延续了HTTP/1的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。

但由于HTTPS已经是大势所趋,而且主流的浏览器Chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以“事实上”的HTTP/2是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用”https”协议名,跑在TLS上面。HTTP/2协议定义了两个字符串标识符:“h2”表示加密的HTTP/2,“h2c”表示明文的HTTP/2。

http3.0新特性

1.HTTP/2 的缺点

主要是底层支撑的 TCP 协议造成的

  • TCP 以及 TCP+TLS建立连接的延时
  • TCP的队头阻塞并没有彻底解决
    上文我们提到在HTTP/2中,多个请求是跑在一个TCP管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求(如下图)。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

HTTP/3简介

Google 在推SPDY的时候就已经意识到了这些问题,于是就另起炉灶搞了一个基于 UDP 协议的“QUIC”协议,让HTTP跑在QUIC上而不是TCP上。而这个“HTTP over QUIC”就是HTTP协议的下一个大版本,HTTP/3。它在HTTP/2的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题。

QUIC新功能

上面我们提到QUIC基于UDP,而UDP是“无连接”的,根本就不需要“握手”和“挥手”,所以就比TCP来得快。此外QUIC也实现了可靠传输,保证数据一定能够抵达目的地。它还引入了类似HTTP/2的“流”和“多路复用”,单个“流”是有序的,可能会因为丢包而阻塞,但其他“流”不会受到影响。具体来说QUIC协议有以下特点:

  • 实现了类似TCP的流量控制、传输可靠性的功能。
    虽然UDP不提供可靠性的传输,但QUIC在UDP的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性。

  • 实现了快速握手功能
    由于QUIC是基于UDP的,所以QUIC可以实现使用0-RTT或者1-RTT来建立连接,这意味着QUIC可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。

  • 集成了TLS加密功能。
    目前QUIC使用的是TLS1.3,相较于早期版本TLS1.3有更多的优点,其中最重要的一点是减少了握手所花费的RTT个数。

  • 多路复用,彻底解决TCP中队头阻塞的问题
    和TCP不同,QUIC实现了在同一物理连接上可以有多个独立的逻辑数据流(如下图)。实现了数据流的单独传输,就解决了TCP中队头阻塞的问题。
    逻辑数据流

如何理解 HTTP 的请求方法?

有哪些请求方法?

http/1.1规定了以下请求方法(注意,都是大写):

  • GET: 通常用来获取资源
  • HEAD: 获取资源的元信息
  • POST: 提交数据,即上传数据
  • PUT: 修改数据
  • DELETE: 删除资源(几乎用不到)
  • CONNECT: 建立连接隧道,用于代理服务器
  • OPTIONS: 列出可对资源实行的请求方法,用来跨域请求
  • TRACE: 追踪请求-响应的传输路径

GET 和 POST 有什么区别?

从缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
从编码的角度,GET 只能进行 URL 编码,只能接收 ASCII 字符,而 POST 没有限制。
从参数的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
从幂等性的角度,GET是幂等的,而POST不是。(幂等表示执行相同的操作,结果也是相同的)
从TCP的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)

简要概括一下 HTTP 的特点?HTTP 有哪些缺点?

HTTP 的特点概括如下:

  • 灵活可扩展,主要体现在两个方面。一个是语义上的自由,只规定了基本格式,比如空格分隔单词,换行分隔字段,其他的各个部分都没有严格的语法限制。另一个是传输形式的多样性,不仅仅可以传输文本,还能传输图片、视频等任意数据,非常方便。
  • 可靠传输。HTTP 基于 TCP/IP,因此把这一特性继承了下来。这属于 TCP 的特性
  • 请求-应答。也就是一发一收、有来有回, 当然这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色
  • 无状态。这里的状态是指通信过程的上下文信息,而每次 http 请求都是独立、无关的,默认不需要保留状态信息

http缺点

  • 无状态
    所谓的优点和缺点还是要分场景来看的,对于 HTTP 而言,最具争议的地方在于它的无状态。

在需要长连接的场景中,需要保存大量的上下文信息,以免传输大量重复的信息,那么这时候无状态就是 http 的缺点了。

但与此同时,另外一些应用仅仅只是为了获取一些数据,不需要保存连接上下文信息,无状态反而减少了网络开销,成为了 http 的优点。

  • 明文传输
    即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。
  • 队头阻塞问题
    当 http 开启长连接时,共用一个 TCP 连接,同一时刻只能处理一个请求,那么当前请求耗时过长的情况下,其它的请求只能处于阻塞状态,也就是著名的队头阻塞问题

如何理解 HTTP 代理?

我们知道在 HTTP 是基于请求-响应模型的协议,一般由客户端发请求,服务器来进行响应。
当然,也有特殊情况,就是代理服务器的情况。引入代理之后,作为代理的服务器相当于一个中间人的角色,对于客户端而言,表现为服务器进行响应;而对于源服务器,表现为客户端发起请求,具有双重身份。

http3

https

工作过程

  • 客户端向服务器端发送https请求,通知服务端可支持的加密算法
  • 服务端向客户端发送公钥和电子证书以及通知要是用的算法
  • 客户端确认电子证书,验证公钥的有效性,是否过期,是否是对方web服务器所属(证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,直到验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)
  • 客户端利用伪随机数生成对称密钥,然后用公钥加密该对称加密中的密钥,发送给服务器端
  • 服务端用自己的私钥解密,得到密钥,至此Client和Server双方都持有了相同的对称密钥
  • 服务端Server使用对称密钥加密“明文内容A”,发送给Client。
  • Client使用对称密钥解密响应的密文,得到“明文内容A”。
  • Client再次发起HTTPS的请求,使用对称密钥加密请求的“明文内容B”,然后Server使用对称密钥解密密文,得到“明文内容B

tcp

三次握手和四次挥手仿佛是所有面试者挥之不去的阴影,说归说还是得去掌握

TCP协议的基本操作

如果一个Host主动向另一个host发起链接,称之为 SYN: synchroniation 请求同步
如果一个Host主动断开请求,称之为FIN: finish 请求完成

为了保持链接和可靠性约束,TCP协议要保证每一条发出的数据必须给返回

三次握手和四次挥手涉及到的属于

  • ack 确认号
  • seq 通讯初识序列号
  • ACK 代表响应,所有的消息都必须有响应,这是tcp确保稳定性的一环

这里请你思考一个问题:稳定性要求数据无损地传输,也就是说拆包获得数据,又需要恢复到原来的样子。而在复杂的网络环境当中,即便所有的段是顺序发出的,也不能保证它们顺序到达,因此,发出的每一个 TCP 段都需要有序号。这个序号,就是 Sequence Number(Seq)。

Sequence Number 和 Acknowledge Number 的设计,就非常巧妙地利用发送字节数和接收字节数解决了顺序的问题。

三次握手

三次握手细节

1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号帧),这时候Client进入syn_sent状态,表示客户端等待服务器的回复

2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1(x是来自客户端的seq),同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个帧),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号帧)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。(syn-Received)

3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个帧),Client自己的序号seq= x + 1(表示这就是我的第1个帧,相对于第0个帧来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。(Establish)

总结来说就是

  • 客户端向服务端发送SYN, Seq=x
  • 服务端返回SYN,ACK,ack=x+1,seq=y
  • 客户端向服务端发送ACK,ack=y+1,seq=x+1

四次挥手

四次挥手细节

第一次挥手:Client将FIN置为1,发送一个序列号seq = p给Server;进入FIN_WAIT_1状态;
第二次挥手:Server收到FIN之后,发送一个ACK=1,acknowledge number=收到的序列号p+1;进入CLOSE_WAIT状态。此时客户端已经没有要发送的数据了,但仍可以接受服务器发来的数据。
第三次挥手:Server将FIN置1,发送一个序列号给Client:seq=q;同时把ack=p + 1, ACK=1 一起发给客户端,服务器进入LAST_ACK状态;
第四次挥手:Client收到服务器的FIN后,进入TIME_WAIT状态;接着将ACK置1,发送一个acknowledge number=序列号q+1 以及seq=p+1给服务器;服务器收到后,确认acknowledge number后,变为CLOSED状态,不再向客户端发送数据。客户端等待2*MSL(报文段最长寿命)时间后,也进入CLOSED状态。完成四次挥手。注意是服务器端先结束先变成CLOSEd状态,客户端等待2个MSL之后才会置为CLOSED状态

  • 总结来说就是
  • 客户端向服务端发送FIN,seq=p
  • 服务端向客户端返回ACK,ack=p+1
  • 服务端向客户端返回FIN,seq=q
  • 客户端向服务端发送ACK,ack=q+1,seq=p+1

客户端TIME_WAIT状态的意义是什么?

第四次挥手时,客户端发送给服务器的ACK有可能丢失,TIME_WAIT状态就是用来重发可能丢失的ACK报文。如果Server没有收到ACK,就会重发FIN,如果Client在2MSL的时间内收到了FIN,就会重新发送ACK并再次等待2MSL,防止Server没有收到ACK而不断重发FIN。 MSL(Maximum Segment Lifetime),指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

TCP为啥要拆包和粘包

在传输层封包不能太大。这种限制,往往是以缓冲区大小为单位的。也就是 TCP 协议,会将数据拆分成不超过缓冲区大小的一个个部分。每个部分有一个独特的名词,叫作 TCP 段(TCP Segment)。

数据经过拆分,然后传输,然后在目的地重组,俗称拆包。所以拆包是将数据拆分成多个 TCP 段传输。那么粘包是什么呢?有时候,如果发往一个目的地的多个数据太小了,为了防止多次发送占用资源,TCP 协议有可能将它们合并成一个 TCP 段发送,在目的地再还原成多个数据,这个过程俗称粘包。所以粘包是将多个数据合并成一个 TCP 段发送

划重点:
拆包: 数据拆分成多个TCP段传输
粘包: 多个数据合并成一个TCP段

TCP 拆包的作用是将任务拆分处理,降低整体任务出错的概率,以及减小底层网络处理的压力。拆包过程需要保证数据经过网络的传输,又能恢复到原始的顺序。这中间,需要数学提供保证顺序的理论依据。TCP 利用(发送字节数、接收字节数)的唯一性来确定封包之间的顺序关系。

粘包是为了防止数据量过小,导致大量的传输,而将多个 TCP 段合并成一个发送。

滑动窗口和流速控制是怎么回事?

动窗口是 TCP 协议控制可靠性的核心。发送方将数据拆包,变成多个TCP 段。然后将数据放入一个拥有滑动窗口的数组,依次发出,仍然遵循先入先出(FIFO)的顺序,但是窗口中的TCP 段会一次性发送。窗口中序号最小的TCP 段如果收到 ACK,窗口就会发生滑动;如果最小序号的TCP 段 长时间没有收到 ACK,就会触发整个窗口的数据重新发送。

另一方面,在多次传输中,网络的平均延迟往往是相对固定的,这样 TCP 协议可以通过双方协商窗口大小控制流速。

udp

开启网络线程到发出一个完整的http请求

DNS解析

如果输入的是域名,需要进行dns解析成IP,大致流程:

如果浏览器有缓存,直接使用浏览器缓存,否则使用本机缓存,再没有的话就是用host
如果本地没有,就向dns域名服务器查询(当然,中间可能还会经过路由,也有缓存等),查询到对应的IP
域名查询时有可能是经过了CDN调度器的(如果有cdn存储功能的话),而且,需要知道dns解析是很耗时的,因此如果解析域名过多,会让首屏加载变得过慢,可以考虑dns-prefetch优化

TCP/IP请求

tcp将http长报文划分为短报文,通过三次握手与服务端建立连接,进行可靠传输。

然后,待到断开连接时,需要进行四次挥手(因为是全双工的,所以需要四次挥手)

tcp/ip的并发限制

浏览器对同一域名下并发的tcp连接是有限制的(2-10个不等)。而且在http1.0中往往一个资源下载就需要对应一个tcp/ip请求

get和post的区别

get和post虽然本质都是tcp/ip,但两者除了在http层面外,在tcp/ip层面也有区别。

get会产生一个tcp数据包,post两个

get请求时,浏览器会把headers和data一起发送出去,服务器响应200(返回数据),
post请求时,浏览器先发送headers,服务器响应100 continue,浏览器再发送data,服务器响应200(返回数据)。

从服务器接收到请求到对应后台接收到请求
负载均衡

用户发起的请求都指向调度服务器(反向代理服务器,譬如安装了nginx控制负载均衡),然后调度服务器根据实际的调度算法,分配不同的请求给对应集群中的服务器执行,然后调度器等待实际服务器的HTTP响应,并将它反馈给用户

参考链接