文章目录
- 1. TCP/IP网络模型有哪几层并做简要介绍?
- 2. 键入网址到网页显示,期间发生了什么?
- 3. 介绍一下域名解析的工作流程?
- 4. MAC发送方和接收方如何确认?
- 5. 路由器和交换机的区别?
- 6. Linux系统是如何收发网络包的?
- 7. HTTP是什么?
- 8. HTTP常见的状态码有哪些?
- 9. HTTP 常见字段有哪些?
- 10. GET和POST有什么区别?
- 11. HTTP 缓存有哪些实现方式?
- 12. 什么是强制缓存?
- 13. 什么是协商缓存?
- 14. 使用ETag字段实现的协商缓存的过程是怎样的?
- 15. HTTP/1.1的优点有哪些?
- 16. HTTP/1.1的缺点有哪些?
- 17. HTTP/1.1的性能如何?
- 18. HTTP与HTTPS有哪些区别?
- 19. HTTPS解决了HTTP的哪些问题?如何解决的?
- 20. HTTPS是如何建立连接的?期间交互了什么?
- 21. 客户端校验数字证书的流程是怎样的?
- 22. HTTPS的应用数据是如何保证完整性的?
- 23. HTTPS一定安全可靠吗?数据会被假基站转发到中间人服务器,然后客户端与中间人服务器进行通信,中间人服务器再与真正的服务器进行通信吗?
- 24. 为什么抓包工具能截取 HTTPS 数据?
- 25. 如何避免被中间人抓取数据?
- 26. HTTP/1.1 相比 HTTP/1.0 提高了什么性能?
- 27. HTTP/1.1 的性能瓶颈有哪些?
- 28. HTTP/2 做了什么优化?
- 29. HTTP/2 有什么缺陷?
- 30. HTTP/3 做了哪些优化?
- 31. HTTP/1.1 该如何优化?
- 32. 如何避免发送 HTTP 请求?
- 33. 如何减少 HTTP 请求次数?
- 34. 如何减少 HTTP 响应的数据大小?
- 35. RSA算法的缺陷
- 36. RSA 和 ECDHE 握手过程的区别
- 37. 既然有RPC了, 为什么还要有HTTP呢?
- 38. HTTP和RPC有什么区别?
- 39. 既然有HTTP协议,为什么还要有WebSocket?
- 40. 怎么建立WebSocket连接?
1. TCP/IP网络模型有哪几层并做简要介绍?
TCP/IP网络模型总共分为四层,从上到下分别为应用层、传输层、网络层以及网络接口层。
应用层:只需要专注于为用户提供应用功能,比如HTTP、FTP、DNS、SMTP等;
传输层:为应用层提供网络支持。在传输层有两个传输协议,分别是TCP和UDP。传输层的报文中会携带端口号,因此接收方可以识别出该报文是发给哪个应用。
网络层:网络层主要负责网络包的分片、路由以及转发功能,将数据从一个设备传输到另一个设备。网络层最常使用的是IP协议,如果IP报文大小超过MTU就会对其分片。
网络接口层:在IP头部的前面加上MAC头部,并封装成数据帧发送到网络中,主要为网络层提供链路级别传输的服务,负责在以太网、WIFI这样的底层网络上发送原始数据包,工作在网卡这个层次,使用MAC地址来标识网络上的设备。
2. 键入网址到网页显示,期间发生了什么?
- 首先浏览器对URL进行解析,生成发送给Web服务器的HTTP请求信息。
- 接着需要委托操作系统将请求消息发送给Web服务器。在发送之前,还需要查询服务器域名对应的IP地址,查询IP地址需要访问DNS服务器。DNS服务器分为根DNS服务器、顶级域DNS服务器、权威DNS服务器。
- 接着HTTP请求报文交给TCP传输层进行传输,在TCP传输数据之前,先要进行三次握手建立连接,三次握手的目的是保证双方都有发送和接收的能力。
TCP头部有源端口和目标端口,告知发给哪个应用。序列号解决包乱序的问题。确认号用于确认对方收到的数据,解决丢包的问题。由于TCP是面向连接的,双方要维护连接的状态,有一些状态位,比如SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接等。头部中还有窗口大小,用于流量控制,表示自己的处理能力。还有校验和、首部长度、紧急指针等。 - TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托网络层的 IP 模块将数据封装成网络包发送给通信对象。在IP头部里面有源IP地址和目标IP地址以及协议号、TTL、片偏移、首部校验和、首部长度以及总长度等。假设客户端有多个网卡时,就会有多个IP地址,这时就需要根据路由表规则,来判断源IP地址。
- 生成了IP头部之后,接下来网络包还需要在IP头部的前面加上MAC头部。在MAC包头里需要发送方MAC地址和接收方目标MAC地址以及协议类型(IP协议/ARP协议),用于两点的传输。
- 网卡驱动获取到网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列,最后网卡会将包转为电信号,通过网线发送出去。
- 数据通过交换机和路由器的转发,最终到达服务器,服务器首先会校验数据包的MAC头部、IP头部,从IP头中协议项知道上层为TCP协议,然后查看TCP头部的序列号是不是自己想要的,如果是就放入缓存中然后返回一个ACK,如果不是就丢弃。通过TCP头部的端口号把数据发给监听该端口的HTTP进程。
- 服务器的HTTP进程把响应数据封装在HTTP响应报文里,然后作为发送端,把数据发给客户端。
- 客户端成功收到响应数据后,向服务器发起TCP四次挥手,至此双方的连接就断开了。
3. 介绍一下域名解析的工作流程?
域名解析的工作流程如下:
- 客户端首先会发出一个DNS请求,向本地DNS服务器询问www.server.com的IP是啥。
- 本地域名服务器收到客户端请求后,如果缓存里的表格能找到www.server.com,则直接返回IP地址。如果没有,则本地DNS服务器就会去问它的根域名服务器。
- 根域名服务器收到本地DNS的请求后,会把对应的顶级域服务器的地址发送给本地DNS服务器。
- 本地DNS收到顶级域服务器地址后,接着向顶级域服务器询问www.server.com的IP。
- 顶级域名服务器会把权威域名服务器的地址响应给本地域名服务器。
- 本地DNS服务器访问权威域名服务器,询问服务器域名对应的IP地址。
- 权威DNS服务器查询后将对应的IP地址返回给本地DNS。
- 本地DNS收到服务器的IP地址后,在本地缓存,然后再把IP地址返回给客户端,客户端和目标端建立连接。
4. MAC发送方和接收方如何确认?
发送方的MAC地址是在网卡生产时写入到ROM里的,只要将这个值读取出来写入到MAC头部就可以了。
在确认接收方的MAC地址时,首先需要在路由表中查询到接收方的IP地址。然后通过ARP协议找到路由器的MAC地址。
ARP协议会在以太网中以广播的形式发送IP地址,询问接收方对应的MAC地址,接收方收到后,会和自己的IP地址进行比对,接着把自己的MAC地址返回给发送方,由此也得到了接收方的MAC地址。操作系统会把本次查询结果放在ARP缓存中,缓存时间就几分钟。通过arp -a
命令来查看ARP缓存的内容。
5. 路由器和交换机的区别?
因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;而交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。
6. Linux系统是如何收发网络包的?
Linux接收网络包的流程:
当网卡接收到一个网络包后,会通过DMA技术将网络包写入到指定的内存地址,也就是写入到Ring Buffer环形缓冲区,接着网卡向CPU发起硬件中断,当CPU收到硬件中断请求后,根据中断表,调用已经注册的硬件中断处理函数。硬件中断处理函数首先暂时屏蔽中断,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,这样可以提高效率,避免CPU不停的被中断。接着,发起软中断,然后恢复刚才屏蔽的中断。当ksoftirqd内核线程收到软中断后,就会来轮询处理数据,从Ring Buffer中获取一个数据帧,用sk_buff表示,从而可以作为一个网络包交给网络协议进行逐层处理。
Linux 发送网络包的流程:
首先,应用程序会调用 Socket 发送数据包的接口,由于这个是系统调用,所以会从用户态陷入到内核态中的 Socket 层,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。接下来,网络协议栈从 Socket 发送缓冲区中取出 sk_buff,并按照 TCP/IP 协议栈从上到下逐层处理。如果使用的是 TCP 传输协议发送数据,那么先拷贝一个新的 sk_buff 副本 ,接着,对 sk_buff 填充 TCP 头。然后交给网络层,在网络层里会做这些工作:选取路由(确认下一跳的 IP)、填充 IP 头、netfilter 过滤、对超过 MTU 大小的数据包进行分片。处理完这些工作后会交给网络接口层处理。网络接口层会通过 ARP 协议获得下一跳的 MAC 地址,然后对 sk_buff 填充帧头和帧尾,接着将 sk_buff 放到网卡的发送队列中。这一些工作准备好后,会触发「软中断」告诉网卡驱动程序,这里有新的网络包需要发送,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 RingBuffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。当数据发送完成以后,其实工作并没有结束,因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 RingBuffer 内存。最后,当收到这个 TCP 报文的 ACK 应答时,传输层就会释放原始的 sk_buff 。
7. HTTP是什么?
HTTP是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。
8. HTTP常见的状态码有哪些?
1xx
类状态码属于提示信息,是协议处理中的一种中间状态。2xx
类状态码表示服务器成功处理了客户端的请求。200 OK
是最常见的成功状态码,表示一切正常。204 No Content
也是常见的成功状态码,其中响应头没有body
数据。206 Partial Content
表示响应返回的body
数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
3xx
类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。301 Moved Permanently
表示永久重定向,需改用新的 URL 再次访问。302 Found
表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
301
和302
都会在响应头里使用字段Location
,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。304 Not Modified
表示资源未修改,缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制。
4xx
类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。400 Bad Request
表示客户端请求的报文有错误,但只是个笼统的错误。403 Forbidden
表示服务器禁止访问资源,并不是客户端的请求出错。404 Not Found
表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
5xx
类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。500 Internal Server Error
是个笼统通用的错误码,服务器发生了错误。501 Not Implemented
表示客户端请求的功能还不支持。502 Bad Gateway
通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。503 Service Unavailable
表示服务器当前很忙,暂时无法响应客户端。
9. HTTP 常见字段有哪些?
Host
字段:客户端发送请求时,用来指定服务器的域名。Connection
字段:常用于客户端要求服务器使用HTTP 长连接机制,需要指定Connection
首部字段的值为Keep-Alive
。Content-Length
字段:服务器在返回数据时,会有Content-Length
字段,表明本次回应的数据长度。Content-Type
字段:用于服务器回应时,告诉客户端本次传输的数据格式。Accept
字段:客户端请求的时候,可以使用Accept
字段声明自己可以接受的数据格式。Content-Encoding
字段:Content-Encoding
字段说明数据的压缩方法,表示服务器返回的数据使用的压缩格式。Accept-Encoding
字段:客户端在请求时,用Accept-Encoding
字段说明自己可以接受的压缩方法。
10. GET和POST有什么区别?
- GET的语义是从服务器获取指定的资源;POST 的语义是根据报文body对指定的资源做出处理,具体的处理方式视资源类型而不同。
- GET请求的参数位置一般是写在URL中,URL规定只能支持ASCII码,所以GET请求的参数只允许 ASCII 字符 ;POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据。
- GET请求时,浏览器会对 URL 的长度有限制;而POST请求时,浏览器不会对body大小做限制。
- GET方法是安全、幂等、可被缓存的;POST 不安全,不幂等,(大部分实现)不可缓存。
11. HTTP 缓存有哪些实现方式?
HTTP 缓存有两种实现方式,分别是强制缓存和协商缓存。
12. 什么是强制缓存?
强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动权在于浏览器这边。
强缓存是利用两个 HTTP 响应头部(Response Header)字段Cache-Control
和Expires
实现的,它们都用来表示资源在客户端缓存的有效期。Cache-Control
的优先级高于 Expires
,所以建议使用Cache-Control
来实现强缓存。
强缓存的具体实现流程:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在响应头部加上
Cache-Control
,Cache-Control
中设置了过期时间大小; - 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与
Cache-Control
中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器; - 服务器再次收到请求后,会再次更新响应头部的
Cache-Control
。
13. 什么是协商缓存?
协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。
协商缓存可以基于两种头部来实现。第一种是基于请求头部中的 If-Modified-Since
字段与响应头部中的 Last-Modified
字段实现;第二种是基于请求头部中的 If-None-Match
字段与响应头部中的 ETag
字段。
协商缓存这两个字段都需要配合强制缓存中Cache-Control
字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
14. 使用ETag字段实现的协商缓存的过程是怎样的?
当使用 ETag
字段实现的协商缓存的过程:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在响应头部加上
ETag
唯一标识,这个唯一标识的值是根据当前请求的资源生成的; - 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期:
- 如果没有过期,则直接使用本地缓存;
- 如果缓存过期了,会在响应头部加上
If-None-Match
字段,该字段的值就是ETag
唯一标识;
- 服务器再次收到请求后,会根据请求中的
If-None-Match
值与当前请求的资源生成的唯一标识进行比较:- 如果值相等,则返回
304 Not Modified
,不会返回资源; - 如果不相等,则返回
200
状态码和返回资源,并在响应头部加上新的ETag
唯一标识;
- 如果值相等,则返回
- 如果浏览器收到
304
的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。
15. HTTP/1.1的优点有哪些?
HTTP1.1最突出的优点是简单、灵活和易于扩展、应用广泛和跨平台。
- 简单:HTTP基本的报文格式是
header + body
,头部信息也是key-value
简单文本格式,易于理解。 - 灵活和易于扩展:协议中的字段允许自定义和扩充,同时由于HTTP工作在应用层,则它的下层可以随意变化,比如HTTPS就是在HTTP与TCP层之间增加了TLS安全传输层,HTTP1.1和HTTP2.0传输协议使用TCP协议,而HTTP3.0传输协议改用了UDP协议。
- 应用广泛和跨平台:台式机和手机的很多APP都采用了HTTP1.1。
16. HTTP/1.1的缺点有哪些?
HTTP协议有优缺点一体的双刃剑,分别为无状态、明文传输,同时还有一大缺点不安全。
- 无状态
- 好处:服务器不需要额外的资源来记录状态信息,能减轻服务器的负担。
- 坏处:由于服务器没有记录HTTP的状态信息,在完成有关联性的操作是会非常麻烦。
对于无状态的问题,比较简单的解决方法是使用Cookie
技术,在客户端第一次请求后,服务器返回的响应报文中携带一个装有客户信息的Cookie
,后续客户端再次请求服务器时,在请求报文中带上Cookie
,服务器检查Cookie
即可。
- 明文传输
- 好处:明文意味着在传输过程中的信息方便阅读,可以直接通过抓包查看报文信息,便于我们的调试工作。
- 坏处:HTTP的所有信息明文传输,很容易被窃取。
- 不安全
- 通信使用明文,因此内容可能会被窃听;
- 不验证通信方的身份,因此有可能遭遇伪装;
- 无法证明报文的完整性, 因此报文有可能被篡改。
17. HTTP/1.1的性能如何?
HTTP协议是基于TCP/IP,并且使用了请求-应答的通信模式,所以性能的关键也在这两点上。
- 长连接
HTTP/1.1 提出了长连接的通信方式好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。
持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。 - 管道网络传输
管道网络传输可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应。所以,HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。 - 队头阻塞
当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会导致客户端一直无法得到响应数据,这也就是队头阻塞。
18. HTTP与HTTPS有哪些区别?
HTTP
是超文本传输协议,信息是明文传输,存在安全风险的问题;HTTPS
则解决HTTP
不安全的缺陷,在TCP
和HTTP
网络层之间加入了SSL/TLS
安全协议,使得报文能够加密传输。HTTP
连接建立相对简单,TCP
三次握手之后便可进行HTTP
的报文传输。而HTTPS
在TCP
三次握手之后,还需进行SSL/TLS
的四次握手过程,才可进入加密报文传输。- 两者的默认端口不一样,
HTTP
默认端口号是80
,HTTPS
默认端口号是443
。 HTTPS
协议需要向CA
(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
19. HTTPS解决了HTTP的哪些问题?如何解决的?
HTTP 由于是明文传输,所以安全上存在以下三个风险:窃听风险,篡改风险,冒充风险。
HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS
协议,可以很好的解决了上述的风险:
- 信息加密:交互信息无法被窃取。
- 校验机制:无法篡改通信内容。
- 身份证书:防止冒充风险。
HTTPS采取的解决办法:
- HTTPS 采用了对称加密和非对称加密结合的混合加密的方式实现信息的机密性,解决了窃听的风险。
在通信建立前采用非对称加密的方式交换会话密钥,后续就不再使用非对称加密;在通信过程中全部使用对称加密的会话密钥的方式加密明文数据。 - 摘要算法的方式来保证数据的完整性,解决了数据被篡改的风险。在计算机里会用摘要算法(哈希函数)来计算出内容的哈希值,这个哈希值是唯一的,且无法通过哈希值推导出内容。服务器使用私钥对哈希值进行加密生成数字签名,然后把内容和数字签名一同发送给客户端,客户到收到消息后,如果能用公钥对数字签名解密,同时得到的哈希值与对收到的内容做哈希运算得到的哈希值进行比对,如果相同就表明消息是完整的,否则,说明消息可能被篡改过。
- 通过数字证书的方式来保证服务器公钥的身份,解决身份冒充的风险。
20. HTTPS是如何建立连接的?期间交互了什么?
HTTPS建立连接的过程中,首先需要经过TCP三次握手连接,接着是TLS四次握手阶段,使用不同的密钥交换算法,TLS握手流程也会不一样的,现在常用的密钥交换算法有RSA
算法和ECDHE
算法。
基于RSA算法的TLS握手过程如下图:
TLS协议建立的详细流程:
- ClientHello
首先,由客户端向服务器发起加密通信请求,也就是ClientHello
请求。
在这一步,客户端主要向服务器发送以下信息:
(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数(Client Random
),后面用于生成会话秘钥条件之一。
(3)客户端支持的密码套件列表,如 RSA 加密算法。 - SeverHello
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:
(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数(Server Random
),也是后面用于生产会话秘钥条件之一。
(3)确认的密码套件列表,如RSA
加密算法。
(4)服务器的数字证书。 - 客户端回应
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
(1)一个随机数(pre-master key
)。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面这一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random
、Server Random
、pre-master key
),接着就用双方协商的加密算法,各自生成本次通信的会话秘钥。 - 服务器的最后回应
服务器收到客户端的第三个随机数(pre-master key
)之后,通过协商的加密算法,计算出本次通信的会话秘钥。
然后,向客户端发送最后的信息:
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
21. 客户端校验数字证书的流程是怎样的?
CA 签发证书的过程,如上图左边部分:
- 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
- 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
- 最后将 Certificate Signature 添加在文件证书上,形成数字证书;
客户端校验服务端的数字证书的过程,如上图右边部分:
- 首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
- 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
- 最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。
但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向CA申请的证书一般不是根证书签发的,而是由中间证书签发的。证书的验证过程是从根证书信任中间证书,然后中间证书信任服务器的证书,一层层的验证就构成了一条信任链路。
使用证书链的目的是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。
22. HTTPS的应用数据是如何保证完整性的?
TLS 在实现上分为握手协议和记录协议两层:
- TLS 握手协议就是我们前面说的 TLS 四次握手的过程,负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(即 HTTP 数据);
- TLS 记录协议负责保护应用程序数据并验证其完整性和来源,所以对 HTTP 数据加密是使用记录协议。
TLS 记录协议主要负责消息(HTTP 数据)的压缩,加密及数据的认证。具体过程如下:
- 首先,消息被分割成多个较短的片段,然后分别对每个片段进行压缩。
- 接下来,经过压缩的片段会被加上消息认证码(MAC 值,这个是通过哈希算法生成的),这是为了保证完整性,并进行数据的认证。通过附加消息认证码的 MAC 值,可以识别出篡改。与此同时,为了防止重放攻击,在计算消息认证码时,还加上了片段的编码。
- 再接下来,经过压缩的片段再加上消息认证码会一起通过对称密码进行加密。
- 最后,上述经过加密的数据再加上由数据类型、版本号、压缩后的长度组成的报头就是最终的报文数据。
23. HTTPS一定安全可靠吗?数据会被假基站转发到中间人服务器,然后客户端与中间人服务器进行通信,中间人服务器再与真正的服务器进行通信吗?
从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于,中间人能够窃听了浏览器与服务端之间的 HTTPS 请求和响应的数据。
但是要发生这种场景是有前提的,前提是用户点击接受了中间人服务器的证书。
HTTPS 协议本身到目前为止还是没有任何漏洞的,即使你成功进行中间人攻击,本质上是利用了客户端的漏洞(用户点击继续访问或者被恶意导入伪造的根证书),并不是 HTTPS 不够安全。
24. 为什么抓包工具能截取 HTTPS 数据?
很多抓包工具 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。
使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。
抓包工具能够抓包的关键是客户端会往系统受信任的根证书列表中导入抓包工具生成的证书,而这个证书会被浏览器信任,也就是抓包工具给自己创建了一个认证中心 CA,客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。
25. 如何避免被中间人抓取数据?
通过 HTTPS 双向认证来避免被中间人抓取数据。
如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。
26. HTTP/1.1 相比 HTTP/1.0 提高了什么性能?
- 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
- 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
27. HTTP/1.1 的性能瓶颈有哪些?
- 请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大,只能压缩 Body 的部分;
- 发送冗长的首部,每次互相发送相同的首部造成的浪费较多;
- 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞;
- 没有请求优先级控制;
- 请求只能从客户端开始,服务器只能被动响应。
28. HTTP/2 做了什么优化?
HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。
那 HTTP/2 相比 HTTP/1.1 性能上的改进:
- 头部压缩
HTTP/2会压缩头部,利用HPACK
算法,在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。 - 二进制格式
HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧(Headers Frame)和数据帧(Data Frame)。 - 并发传输
HTTP/2 引入了Stream
的概念,1 个 TCP 连接包含多个 Stream,Stream 里可以包含 1 个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成。
针对不同的 HTTP 请求用独一无二的 Stream ID 来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错地发送请求和响应,客户端收到后,会根据相同的 Stream ID 有序组装成 HTTP 消息。 - 服务器推送
客户端和服务器双方都可以建立 Stream, Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。
29. HTTP/2 有什么缺陷?
HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,但是HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当前 1 个字节数据没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。
一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
30. HTTP/3 做了哪些优化?
HTTP/2 队头阻塞的问题是因为 TCP,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
HTTP3通过基于UDP的QUIC
协议实现了类似TCP的可靠性传输。
QUIC
有以下 3 个特点:无队头阻塞、更快的连接建立、连接迁移。
- 无队头阻塞
QUIC 协议也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。
QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。 - 更快的连接建立
对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,必须先 TCP 握手,再 TLS 握手。
HTTP/3 在传输数据前虽然需要 QUIC 协议握手,但是这个握手过程只需要 1 RTT,握手的目的是为确认双方的连接 ID,连接迁移就是基于连接 ID 实现的。
但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商。甚至,在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。 - 连接迁移
基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。
那么当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接。
而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。
31. HTTP/1.1 该如何优化?
- 尽量避免发送 HTTP 请求;
- 在需要发送 HTTP 请求时,考虑如何减少请求次数;
- 减少服务器的 HTTP 响应的数据大小。
32. 如何避免发送 HTTP 请求?
避免发送 HTTP 请求的方法就是通过缓存技术,客户端会把第一次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,而响应作为 value,两者形成映射关系。
如果客户端的缓存没有过期或者服务器的Etag摘要未发生改变,则说明缓存是可以继续使用的,那么服务器仅返回不含有包体的 304 Not Modified
响应,告诉客户端仍然有效,这样就可以减少响应资源在网络中传输的延时。
33. 如何减少 HTTP 请求次数?
可以从3个方面来减少HTTP请求次数:减少重定向请求次数、合并请求、延迟发送请求。
- 减少重定向请求次数
如果重定向的工作交由代理服务器完成,就能减少 HTTP 请求次数了,而且当代理服务器知晓了重定向规则后,可以进一步减少消息传递次数。 - 合并请求
合并请求的方式就是合并资源,以一个大资源的请求替换多个小资源的请求。
合并请求的好处:1、减少了重复发送的 HTTP 头部;2、减少了 TCP 连接的数量,因而省去了 TCP 握手和慢启动过程耗费的时间;3、减少请求次数从而减少了网络的开销。
合并请求的缺点:当大资源中的某一个小资源发生变化后,客户端必须重新下载整个完整的大资源文件,这显然带来了额外的网络消耗。 - 延迟发送请求
可以通过按需获取的方式,来减少第一时间的 HTTP 请求次数。
请求网页的时候,没必要把全部资源都获取到,而是只获取当前用户所看到的页面资源,当用户向下滑动页面的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。
34. 如何减少 HTTP 响应的数据大小?
我们可以考虑对响应的资源进行压缩,这样就可以减少响应的数据大小,从而提高网络传输的效率。
35. RSA算法的缺陷
使用RSA密钥协商算法的最大问题是不支持前向保密。
因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
36. RSA 和 ECDHE 握手过程的区别
- RSA 密钥协商算法不支持前向保密,ECDHE 密钥协商算法支持前向保密;
- 使用了 RSA 密钥协商算法,TLS 完成四次握手后,才能进行应用数据传输,而对于 ECDHE 算法,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,节省了一个消息的往返时间;
- 使用 ECDHE, 在 TLS 第 2 次握手中,会出现服务器端发出的
Server Key Exchange
消息,而 RSA 握手过程没有该消息。
37. 既然有RPC了, 为什么还要有HTTP呢?
纯裸 TCP 是能收发数据,但它是个无边界的数据流,上层需要定义消息格式用于定义消息边界。于是就有了各种协议,HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。
HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了,B/S 和 C/S 在慢慢融合。很多软件同时支持多端,所以对外一般用 HTTP 协议,而内部集群的微服务之间则采用 RPC 协议进行通讯。
38. HTTP和RPC有什么区别?
- 服务发现
客户端与服务器建立连接的前提是需要知道服务器的IP地址和端口。这个找到服务对应的IP端口的过程,就是服务发现。
在HTTP中,通过DNS服务解析域名,得到服务器的IP地址,默认80端口。
而对于RPC而言,一般会有专门的中间服务去保存服务名和IP信息,想要访问某个服务,就去这些中间服务去获得 IP 和端口信息。 - 底层连接形式
HTTP/1.1协议,在建立底层TCP连接之后会保存这个连接,之后的请求和响应都会复用这条连接。
RPC协议也是建立TCP长连接,但是RPC协议一般会采用连接池,减少TCP创建和销毁的开销。
由于连接池有利于提升网络请求性能,所以不少编程语言的网络库里都会给 HTTP 加个连接池,比如 Go 就是这么干的。 - 传输的内容
基于TCP传输的消息,消息包含消息头Header和消息体Body。
Header
是用于标记一些特殊信息,其中最重要的是消息体长度。
Body
则是真正传输的内容,而这些内容只能是二进制01串,计算机只认识二进制代码。所以TCP传输字符串和数字问题不大,因为字符串可以转成编码再变成 01 串,而数字本身也能直接转为二进制。但结构体需要通过Json
或者Protobuf
等技术将它也转为二进制 01 串。
这个将结构体转为二进制数组的过程就叫序列化,反过来将二进制数组复原成结构体的过程叫反序列化。
对于主流的 HTTP/1.1,它传的内容以字符串为主,Header 和 Body 都是如此。在 Body 这块,它使用 Json
来序列化结构体数据。以key-value
的方式存储数据,内容非常多的冗余,传输效率不高。
而RPC采用Protobuf
或者其他序列化协议去保存结构体数据,同时也不需要像HTTP那样考虑各种浏览器行为,比如302重定向等。因此,RPC相比于HTTP/1.1性能更好一些,这也是在公司内部微服务中抛弃HTTP,选择使用RPC的最主要原因。
39. 既然有HTTP协议,为什么还要有WebSocket?
- TCP 协议本身是全双工的,但我们最常用的 HTTP/1.1,虽然是基于 TCP 的协议,但它是半双工的,对于大部分需要服务器主动推送数据到客户端的场景,都不太友好,因此我们需要使用支持全双工的 WebSocket 协议。
- 在 HTTP/1.1 里,只要客户端不问,服务端就不答。基于这样的特点,对于登录页面这样的简单场景,可以使用定时轮询或者长轮询的方式实现服务器推送(comet)的效果。
- 对于客户端和服务端之间需要频繁交互的复杂场景,比如网页游戏,都可以考虑使用 WebSocket 协议。
- 正因为各个浏览器都支持 HTTP协 议,所以 WebSocket 会先利用HTTP协议加上一些特殊的 header 头进行握手升级操作,升级成功后就跟 HTTP 没有任何关系了,之后就用 WebSocket 的数据格式进行收发数据。
40. 怎么建立WebSocket连接?
浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信。
- 如果此时是普通的
HTTP
请求,那后续双方就继续用普通HTTP
协议进行交互。 - 如果这时候是想建立
WebSocket
连接,就会在HTTP
请求里带上一些特殊的header
头,如下:
这些 header 头的意思是,浏览器想升级协议(Connection: Upgrade Upgrade: WebSocket Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n
Connection: Upgrade
),并且想升级成 WebSocket 协议(Upgrade: WebSocket
)。同时带上一段随机生成的 base64 码(Sec-WebSocket-Key
),发给服务器。
如果服务器正好支持升级成 WebSocket 协议。就会根据客户端生成的 base64 码,用某个公开的算法变成另一段字符串,放在 HTTP 响应的Sec-WebSocket-Accept
头里,同时带上101
状态码(表示协议切换),发回给浏览器。HTTP 的响应如下:
之后,浏览器也用同样的公开算法将base64码转成另一段字符串,如果这段字符串跟服务器传回来的字符串一致,那验证通过。HTTP/1.1 101 Switching Protocols\r\n Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n Upgrade: WebSocket\r\n Connection: Upgrade\r\n
就这样经历了一来一回两次 HTTP 握手,WebSocket就建立完成了,后续双方就可以使用 webscoket 的数据格式进行通信了。