面试系列TCP/HTTP协议
Posted 放开我我还能学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试系列TCP/HTTP协议相关的知识,希望对你有一定的参考价值。
从网络和书籍中整理了如下 TCP 和 HTTP 协议相关面试题,如有收获请三连!
1、DNS 解析
按顺序依次查找,找到就返回。
浏览器缓存
操作系统缓存
路由器缓存
ISP DNS 缓存
根域名服务器查询
2、TCP 连接(三次握手)
3、TLS 握手
4、发送 HTTP 请求
5、HTTP 服务器处理请求
6、浏览器解析 HTML 和 CSS
7、浏览器渲染页面
介绍一下 TCP/IP 四层模型?
应用层
应用层一般是我们编写的应用程序,决定了向用户提供的应用服务。应用层可以通过系统调用与传输层进行通信。应用层协议有如:FTP、DNS、HTTP、HTTPS 等。
传输层
传输层通过系统调用向应用层提供数据传输能力。传输层协议有如 TCP、UDP。
网络层
网络层用来处理在网络上流动的数据包,数据包是网络传输的最小数据单位。网络层规定了通过怎样的路径把数据包传输给对方。网络层协议有如:IP、ARP 等。
链路层
链路层用来处理连接网络的硬件部分。
为什么需要 TCP 协议?
网络层的 IP 协议是不可靠的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。
因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
什么是 TCP ?
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
面向连接
要求一对一连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的。
可靠的
无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端。
字节流
消息是没有边界的,所以无论我们消息有多大都可以进行传输。并且消息是有序的,当前一个消息没有收到的时候,即使它先收到了后面的字节已经收到,那么也不能扔给应用层去处理,同时对重复的报文会自动丢弃。
什么是 TCP 连接?
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为 TCP 连接。
建立一个 TCP 连接是需要客户端与服务器端达成以下三个共识:
序列号:用来解决乱序问题等
窗口大小:用来做流量控制
如何唯一确定一个 TCP/UDP 连接?
TCP/UDP 四元组可以唯一的确定一个连接,四元组包括如下:
源端口
目的端口
为什么 TCP/UDP 的端口号最大只能为 65535?
因为在 TCP/UDP 协议头部中,只定义了 16 位来存储源端口号和目的端口号。因此最大为 2^16 - 1 = 65535。
UDP 和 TCP 的区别和应用场景。
1、连接
TCP 是面向连接的传输层协议,传输数据前先要建立连接。
UDP 是面向无连接的传输层协议,即刻传输数据。
2、服务对象
TCP 是一对一的两点服务,即一条连接只有两个端点。
UDP 支持一对一、一对多、多对多的交互通信。
3、可靠性
TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
UDP 是尽最大努力交付,不保证可靠交付数据。
4、拥塞控制、流量控制
TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
5、首部开销
TCP 首部长度较长,会有一定的开销。
UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
应用场景
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
FTP 文件传输
HTTP / HTTPS
由于 UDP 面向无连接,它可以随时发送数据,再加上 UDP 本身的处理既简单又高效,因此经常用于:
包总量较少的通信,如 DNS 、SNMP 等
视频、音频等多媒体通信
广播通信
说说 TCP 的三次握手。
一开始,客户端和服务端都处于 CLOSED
状态。先是服务端主动监听某个端口,处于 LISTEN
状态。
第一次握手:客户端随机初始化序列号 seq=x
,将此序号置于 TCP 首部的「序列号」字段中,同时把 SYN
标志位置为 1
,表示 SYN
报文。接着把第一个 SYN
报文发送给服务端,表示向服务端发起连接,之后客户端处于 SYN-SENT
状态。,等待服务端确认。
第二次握手:服务端收到数据包后由标志位 SYN=1
得知客户端请求建立连接,于是服务端也随机初始化自己的序列号 seq=y
,将此序列号填入 TCP 首部的「序列号」字段中,其次把 TCP 首部的「确认应答号」字段填入 x+1
。接着把 SYN
和 ACK
标志位置为 1
,最后把该报文发给客户端以确认连接请求,之后服务端处于 SYN-RCVD
状态。
第三次握手:客户端收到服务端确认报文后,首先检查确认报文 TCP 首部的「确认应答号」字段 ack
是否为 x+1
,标志位 ACK
是否为 1
,如果正确则将应答报文的标志位 ACK
置为 1
,其次「确认应答号」字段填入 y+1
,并将该数据包发送给服务端,服务端收到应答报文后检查该报文 TCP 首部的「确认应答号」字段 ack
是否为 y+1
,标志位 ACK
是否为 1
,如果正确则连接建立成功,客户端和服务端进入 ESTABLISHED
状态,完成三次握手,随后就可以开始传输数据。
为什么 TCP 是三次握手?不是两次、四次?
1、避免历史连接
网络环境是错综复杂的,并不是先发送的数据包,就先到达目标主机,可能会由于网络拥堵等原因,使旧的数据包先到达目标主机,那么这种情况下 TCP 三次握手如何避免呢?
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:
一个旧 SYN 报文比新的 SYN 报文早到达了服务端;
那么此时服务端就会回一个
SYN + ACK
报文给客户端;客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送
RST
报文给服务端,表示中止这一次连接。
如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发送第三次报文时,客户端因有足够的上下文来判断当前连接是否是历史连接:
如果是历史连接(序列号过期或超时),则第三次握手发送的报文是
RST
报文,以此中止历史连接;如果不是历史连接,则第三次发送的报文是
ACK
报文,通信双方就会成功建立连接。
2、同步双方初始序列号
TCP 协议的通信双方, 都必须维护一个序列号, 序列号是可靠传输的一个关键因素,它的作用有:
接收方可以去除重复的数据
接收方可以根据数据包的序列号按序接收
可以标识发送出去的数据包中, 哪些是已经被对方收到的
可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN
报文的时候,需要服务端回一个 ACK
应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了三次握手。而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
3、避免资源浪费
如果只有两次握手,当客户端的 SYN
请求连接在网络中阻塞,客户端没有接收到 ACK
报文,就会重新发送 SYN
,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK
确认信号,所以每收到一个 SYN
就只能先主动建立一个连接。如果客户端的 SYN
阻塞了,重复发送多次 SYN
报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN
报文,而造成重复分配资源。
总结
TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。
不使用两次握手和四次握手的原因:
两次握手:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
四次握手:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
说说 TCP 的四次挥手。
第一次挥手:客户端打算关闭连接,此时会发送一个 TCP 首部 FIN
标志位被置为 1
的报文,也即 FIN
报文,之后客户端进入 FIN_WAIT_1
状态。
第二次挥手:服务端收到该报文后,就向客户端发送 ACK
应答报文,接着服务端进入 CLOSED_WAIT
状态。客户端收到服务端的 ACK
应答报文后,进入 FIN_WAIT_2
状态。
第三次挥手:等待服务端处理完数据后,也向客户端发送 FIN
报文,之后服务端进入 LAST_ACK
状态。
第四次挥手:客户端收到服务端的 FIN
报文后,回一个 ACK
应答报文,之后进入 TIME_WAIT
状态。服务端收到了 ACK
应答报文后,就进入了 CLOSE
状态,至此服务端已经完成连接的关闭。客户端经过等待计时器时间 2MSL
后,自动进入 CLOSE
状态,至此客户端也完成连接的关闭。
注意:主动关闭连接的,才有 TIME_WAIT 状态。
为什么客户端在
TIME-WAIT
状态必须等待 2MSL 的时间?
当客户端发送出最后的 ACK 报文,但该 ACK 报文可能丢失,服务端如果没有收到 ACK 报文,将不断重复发送 FIN 报文,所以客户端不能立即关闭,它必须确认服务端接收到了该 ACK 报文。客户端会在发送出 ACK 报文之后进入到 TIME_WAIT 状态。客户端会设置一个计时器,等待 2MSL 的时间。如果在该时间内再次收到 FIN 报文,那么客户端会重发 ACK 报文并再次等待 2MSL。
为什么挥手需要四次?
因为在挥手过程中,客户端关闭连接和服务端关闭连接是独立的,可能客户端没有数据了想关闭连接,但是服务端可能还在处理数据,那么不能立马关闭。
也就是说,客户端发起的一次关闭请求只保证自己关闭,而不管服务端是否能关闭。那服务端也是用理。这里双方都一来一回就是四次挥手。
什么是 Socket?
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个 Socket。
Socket 的本质是编程接口,是对 TCP/IP 的封装,处于应用层和传输层之间的中间软件抽象层。TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。
基于 TCP 的 Socket 连接是如何建立的?
1、服务端和客户端初始化 socket
,得到文件描述符;
3、服务端调用 listen
,进行监听;
4、服务端调用 accept
,等待客户端连接;
6、服务端 accept
返回用于传输的 socket
的文件描述符;
7、客户端调用 write
写入数据;服务端调用 read
读取数据;
8、客户端断开连接时,会调用 close
,那么服务端 read
读取数据的时候,就会读取到了 EOF
,待处理完数据后,服务端调用 close
,表示连接关闭。
这里需要注意的是,服务端调用 accept
时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。
所以,监听的 socket 和真正用来传送数据的 socket,是两个 socket,一个叫作监听 socket,另一个叫作已完成连接 socket。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据。
注意:客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回是在三次握手成功之后。
如何理解 HTTP 协议是无连接,无状态的?
无连接:限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
无状态:协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息。
说下 GET 和 POST 的区别。
1、长度限制
GE T请求在 URL 中传送的参数是有长度限制的,而POST没有。
2、参数类型
GET 请求只能进行 URL 编码,而 POST 支持多种编码方式。
3、参数传递
GET 请求的参数通过 URL 传递,这样就暴露了。而 POST 请求的参数是放在请求体中的,不会暴露在外面。
4、缓存
GET 请求会被浏览器主动缓存,而 POST 不会,除非手动设置。
5、安全
安全是指请求方法不会破坏服务器上的资源。
GET 请求是安全的,因为它是只读操作。而 POST 因为是新增或提交数据的操作,会修改服务器上的资源,所以是不安全的。
6、幂等
幂等是指多次执行相同的操作,结果都是相同的。
GET 请求是幂等的,无论操作多少次,结果都是相同的。POST 请求多次提交数据就会创建多个资源,所以不是幂等的。
7、数据包
GET 请求产生一个 TCP 数据包,而 POST 产生两个 TCP 数据包。
对于 GET 方式的请求,浏览器会把 header 和 data 一并发送出去。而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data。
说说 HTTP 数据传输过程。
发送端发送数据时,数据会从上层传输到下层,且每经过一层都会打上该层的头部信息。
接收端接收数据时,数据会从下层传输到上层,且每经过一层都会删除该层的头部信息。
说说 HTTP 的内容协商机制。
内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的基准。
内容协商有以下 3 种类型:
服务器驱动协商
由服务器端进行内容协商。以请求的首部字段为参考,在服务器端自动处理。但对用户来说,以浏览器发送的信息作为判定的依据,并不一定能筛选出最优内容。
客户端驱动协商
由客户端进行内容协商的方式。用户从浏览器显示的可选项列表中手动选择。还可以利用 javascript 脚本在 Web 页面上自动进行上述选择。比如按 OS 的类型或浏览器类型,自行切换成 PC 版页面或手机版页面。
透明协商
是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。
HTTP 有哪几种首部字段类型?
HTTP 首部字段类型根据实际用途分为以下 4 种类型:
通用首部字段
请求报文和响应报文两方都会使用的首部。
请求首部字段
从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
响应首部字段
从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
实体首部字段
针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
说说代理、网关、隧道的区别。
代理
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
代理服务器的基本行为就是接收客户端发送的请求后转发给其他服务器。代理不改变请求 URI,会直接发送给前方持有资源的目标服务器。持有资源实体的服务器被称为源服务器。从源服务器返回的响应经过代理服务器后再传给客户端。
代理分为两种方式:
缓存代理
代理转发响应时,缓存代理(Caching Proxy)会预先将资源的副本(缓存)保存在代理服务器上。当代理再次接收到对相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
透明代理
转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理(Transparent Proxy)。反之,对报文内容进行加工的代理被称为非透明代理。
网关
网关是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。
网关的工作机制和代理十分相似。而网关能使通信线路上的服务器提供非 HTTP 协议服务。
利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。比如,网关可以连接数据库,使用 SQL 语句查询数据。
隧道
隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。
隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
隧道本身不会去解析 HTTP 请求。也就是说,请求保持原样中转给之后的服务器。隧道会在通信双方断开连接时结束。
HTTP 的缺点是什么?
1、通信使用明文(不加密),内容可能会被窃听
需要加密处理防止窃听,常用的有通信加密和内容加密。
通信加密就是利用 SSL 或 TLS 建立安全的通信线路。(TLS 是 SSL 标准化后的产物)
内容加密就是对通信内容本身加密,客户端和服务端需要同时具备加密和解密机制。
2、不验证通信方的身份,因此有可能遭遇伪装
使用 SSL 证书,完成身份认证。
3、无法证明报文的完整性,所以有可能已遭篡改
SSL 提供认证和加密处理及摘要功能。
HTTPS 为何是安全的,它是如何解决上述 HTTP 的缺点?
HTTPS = HTTP + 加密 + 认证 + 完整性保护
1、混合加密
通过混合加密的方式可以保证信息的机密性,解决了窃听的风险。
HTTPS 采用混合加密机制,即采用共享密钥加密(对称加密)和公开密钥加密(非对称加密)两者并用的混合加密机制。
由于共享密钥加密方式速度快,但安全性差;公开密钥加密方式安全性高,但速度慢。因此可以充分利用两者的优势,在交换密钥环节(共享密钥)使用公开密钥加密方式,这样共享密钥就不会被破解,安全性高。之后的建立通信交换报文阶段使用共享密钥加密方式,速度快。
2、摘要算法
摘要算法用来实现完整性,能够为数据生成独一无二的「指纹」,用于校验数据的完整性,解决了篡改的风险。
客户端在发送明文之前会通过摘要算法算出明文的「指纹」,发送的时候把「指纹 + 明文」一同加密成密文后,发送给服务器,服务器解密后,用相同的摘要算法算出发送过来的明文,通过比较客户端携带的「指纹」和当前算出的「指纹」做比较,若「指纹」相同,说明数据是完整的。
3、数字证书
客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
这就存在些问题,如何保证公钥不被篡改?
所以这里就需要借助第三方权威机构 CA
(数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。
HTTP 认证方式有哪些?
BASIC 认证
BASIC 认证的步骤如下:
步骤1:当请求的资源需要 BASIC 认证时,服务器会随状态码 401 Authorization Required ,返回带 WWW-Authenticate 首部字段的响应。
步骤2:接收到状态码 401 的客户端为了通过 BASIC 认证,需要将用户 ID 及密码发送给服务器。发送的字符串内容是由用户 ID 和密码构成,两者中间以冒号:
连接后,再经过 Base64 编码处理。
假设用户 ID 为 guest ,密码是 guest ,连接起来就会形成 guest:guest 这样的字符串。然后经过 Base64 编码,最后的结果即是 Z3Vlc3Q6Z3Vlc3Q= 。把这串字符串写入首部字段 Authorization 后,发送请求。当用户代理为浏览器时,用户仅需输入用户 ID 和密码即可,之后,浏览器会自动完成到 Base64 编码的转换工作。
步骤3:接收到包含首部字段 Authorization 请求的服务器,会对认证信息的正确性进行验证。如验证通过,则返回一条包含 Request-URI 资源的响应。
BASIC 认证虽然采用 Base64 编码方式,但这不是加密处理。不需要任何附加信息即可对其解码。换言之,由于明文解码后就是用户 ID 和密码,在 HTTP 等非加密通信的线路上进行 BASIC 认证的过程中,如果被人窃听,被盗的可能性极高。
另外,除此之外想再进行一次 BASIC 认证时,一般的浏览器却无法实现认证注销操作,这也是问题之一。
BASIC 认证使用上不够便捷灵活,且达不到多数 Web 网站期望的安全性等级,因此它并不常用。
DIGEST 认证
为弥补 BASIC 认证存在的弱点,从 HTTP/1.1 起就有了 DIGEST 认证。DIGEST 认证同样使用质询/响应的方式( challenge/response ),但不会像 BASIC 认证那样直接发送明文密码。
所谓质询响应方式是指,一开始一方会先发送认证要求给另一方,接着使用从另一方那接收到的质询码计算生成响应码。最后将响应码返回给对方进行认证的方式。
因为发送给对方的只是响应摘要及由质询码产生的计算结果,所以比起 BASIC 认证,密码泄露的可能性就降低了。
DIGEST 认证的步骤如下:
步骤1:请求需认证的资源时,服务器会随着状态码 401Authorization Required,返回带 WWW-Authenticate 首部字段的响应。该字段内包含质问响应方式认证所需的临时质询码(随机数,nonce)。
首部字段 WWW-Authenticate 内必须包含 realm 和 nonce 这两个字段的信息。客户端就是依靠向服务器回送这两个值进行认证的。
nonce 是一种每次随返回的 401 响应生成的任意随机字符串。该字符串通常推荐由 Base64 编码的十六进制数的组成形式,但实际内容依赖服务器的具体实现。
步骤2:接收到 401 状态码的客户端,返回的响应中包含 DIGEST 认证必须的首部字段 Authorization 信息。首部字段 Authorization 内必须包含 username、realm、nonce、uri 和 response 的字段信息。其中,realm 和 nonce 就是之前从服务器接收到的响应中的字段。username 是 realm 限定范围内可进行认证的用户名。
uri(digest-uri)即 Request-URI 的值,但考虑到经代理转发后 Request-URI 的值可能被修改,因此事先会复制一份副本保存在 uri 内。response 也可叫做 Request-Digest,存放经过 MD5 运算后的密码字符串,形成响应码。
步骤3:接收到包含首部字段 Authorization 请求的服务器,会确认认证信息的正确性。认证通过后则返回包含 Request-URI 资源的响应。并且这时会在首部字段 Authentication-Info 写入一些认证成功的相关信息。
DIGEST 认证提供了高于 BASIC 认证的安全等级,但是和 HTTPS 的客户端认证相比仍旧很弱。DIGEST 认证提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制。
DIGEST 认证和 BASIC 认证一样,使用上不那么便捷灵活,且仍达不到多数 Web 网站对高度安全等级的追求标准。因此它的适用范围也有所受限。
HTTP/1.1 相比 HTTP/1.0 做了哪些性能上的改善?
1、长连接
早期 HTTP/1.0 性能上的一个很大的问题,那就是每发起一个请求,都要新建一次 TCP 连接(三次握手),增加了通信开销。
为了解决上述 TCP 连接问题,HTTP/1.1 提出了长连接的通信方式,也叫持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。
长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
2、管道网络传输
HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
假如客户端需要请求两个资源。HTTP/1.0 的做法是,在同一个 TCP 连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。管道机制则是允许浏览器同时发出 A 请求和 B 请求。
但是服务器还是按照顺序,先回应 A 请求,完成后再回应 B 请求。如果前面的回应特别慢,后面就会有许多请求排队等着。这称为队头堵塞。
HTTP/1.1 的性能瓶颈:
请求 / 响应头部未经压缩就发送,首部信息越多延迟越大。只能压缩 Body 的部分。
发送冗长的首部。每次互相发送相同的首部造成的浪费较多。
服务器是按请求的顺序响应的,如果服务器响应慢,会导致客户端一直请求不到数据,也就是队头阻塞。
没有请求优先级控制。
请求只能从客户端开始,服务器只能被动响应。
HTTP/2 相比 HTTP/1.1 做了哪些改善?
1、头部压缩
在 HTTP/2 中,使用了 HPACK
压缩算法对传输的 header 进行编码,减少了 header 的大小。并在两端维护了索引表,用于记录出现过的 header,后面在传输过程中就可以传输已经记录过的 header 的键名,对端收到数据后就可以通过键名找到对应的值。
2、二进制传输
HTTP/2 不再像 HTTP/1.1 里的文本传输,而是采用了二进制传输。
头信息和数据都是二进制,并且统称为帧 Frame
:头信息帧和数据帧。
这样虽然对人不友好,但是对计算机非常友好,因为计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率。
3、数据流和优先级
HTTP/2 的数据包不是按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。
每个请求或回应的所有数据包,称为一个数据流 Stream
(流是多个帧组成的)。
每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数。
客户端还可以指定数据流的优先级。优先级高的请求,服务器就先响应该请求。
4、多路复用
HTTP/2 是可以在一个连接中并发请求或回应,而不用按照顺序一一对应,因此也就不会出现队头阻塞问题,降低了延迟,大幅度提高了连接的利用率。
假如在一个 TCP 连接里,服务器收到了客户端 A 和 B 的两个请求,如果发现 A 处理过程非常耗时,于是就回应 A 请求已经处理好的部分,接着回应 B 请求,完成后,再回应 A 请求剩下的部分。
5、服务端推送
HTTP/2 还在一定程度上改善了传统的请求 - 应答工作模式,服务不再是被动地响应,也可以主动向客户端发送消息。
比如在浏览器刚请求 html 的时候,就提前把可能会用到的 JS、CSS 文件等静态资源主动发给客户端,减少延时的等待,也就是服务器推送(Server Push,也叫 Cache Push)。
6、更安全
HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全是强制要求的。
HTTP/3 相比 HTTP/2 做了哪些改善?
HTTP/2 主要的问题在于:多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协议是不知道有多少个 HTTP 请求的。所以一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
对于:
HTTP/1.1 中的管道传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了。
HTTP/2 多请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。
这都是基于 TCP 传输层的问题,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
我们知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议(Quick UDP Internet Connection) 可以实现类似 TCP 的可靠性传输的。
参考资料
[1] https://mp.weixin.qq.com/s/tH8RFmjrveOmgLvk9hmrkw
[2] https://mp.weixin.qq.com/s/bUy220-ect00N4gnO0697A
[3] HTTP权威指南
[4] HTTP2基础教程
往期推荐
以上是关于面试系列TCP/HTTP协议的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud系列十一:SpringCloudStream(SpringCloudStream 简介创建消息生产者创建消息消费者自定义消息通道分组与持久化设置 RoutingKey)(代码片段