《Java 后端面试经》专栏文章索引:
《Java 后端面试经》Java 基础篇
《Java 后端面试经》Java EE 篇
《Java 后端面试经》数据库篇
《Java 后端面试经》多线程与并发编程篇
《Java 后端面试经》JVM 篇
《Java 后端面试经》操作系统篇
《Java 后端面试经》Linux 篇
《Java 后端面试经》设计模式篇
《Java 后端面试经》计算机网络篇
《Java 后端面试经》微服务篇
《Java 后端面试经》计算机网络篇
🚀基础部分
🚁浏览器请求一个网址都经历了哪些过程?
- DNS 解析域名
- 建立 TCP 连接
- 客户端发送 HTTP 请求报文
- 服务器处理请求并返回 HTTP 响应报文
- 浏览器解析渲染,加载页面
- 释放 TCP 连接
🚁DNS 解析域名的过程?
- 浏览器搜索自己的 DNS 缓存
- 若没有,则搜索操作系统中的 DNS 缓存和 hosts 文件
- 若没有,则操作系统将域名发送至本地域名服务器,本地域名服务器查询自己的 DNS 缓存,查找成功则返回结果,否则依次向根域名服务器、顶级域名服务器、权限域名服务器发起查询请求,最终返回 IP 地址给本地域名服务器
- 本地域名服务器将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来
- 操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来
- 浏览器得到域名对应的 IP 地址
🚁Cookie 的作用是什么? Cookie 和 Session 有什么区别?
- Session 数据保存在服务器端,Cookie 数据保存在客户端
- Session 的运行依赖
session id
,而session id
是存在 Cookie 中的,也就是说,如果浏览器禁用了 Cookie ,同时 Session 也会失效(但是可以通过其它方式实现,比如在 url 中传递session_id
) - Cookie 一般用来保存用户的信息,Session 一般用来保存用户的状态(典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。)
- Cookie 存储在客户端中,而 Session 存储在服务器上,相对来说 Session 安全性更高。如果要在 Cookie 中存储一些敏感信息,不要直接写入 Cookie 中,最好能将 Cookie 信息加密,然后使用到的时候再去服务器端解密
🚁Ping 的工作原理?
Ping 是一种计算机网络工具,用来测试数据包能否透过 IP 协议到达特定主机。Ping 的运作原理是向目标主机传出一个 ICMP echo@要求数据包
,并等待接收 echo 回应数据包。程序会按时间和成功响应的次数估算丢失数据包率(丢包率)和数据包往返时间(网络时延,Round-trip delay time)。
🚁OSI 七层协议、TCP/IP 四层协议 和五层协议的体系结构?
TCP/IP 五层模型:应用层、传输层、网络层、数据链路层、物理层:
- 应用层:为应用程序提供交互服务。在互联网中的应用层协议很多,如 FTP、SMTP、POP3、HTTP、DNS、Telnet 协议。
- 传输层:负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议 TCP 和用户数据协议 UDP.
- 网络层:选择合适的路由和交换结点,确保数据及时传送。主要包括 ICMP、ARP、RARP、IP、IGMP、OSPF 协议。
- 数据链路层:在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧,主要包括 PPP、HDLC、SDLC、STP 协议。
- 物理层:实现相邻节点间比特流的透明传输,尽可能屏蔽传输介质和物理设备的差异。
🚁TCP 与 UDP 的区别
TCP 和 UDP 都是属于传输层的协议,主要区别如下:
- TCP 面向连接(如打电话要先拨号建立连接)。UDP 是无连接的,即发送数据之前不需要建立连接。
- TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达。UDP 尽最大努力交付,即不保证可靠交付。
- TCP 面向字节流,实际上是 TCP 把数据看成一连串无结构的字节流。UDP 是面向报文的,UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等)。
- 每一条 TCP 连接只能是点到点的。UDP支持一对一,一对多,多对一和多对多的交互通信。
- TCP 首部开销 20 字节。UDP的首部开销小,只有8个字节。
- TCP 的逻辑通信信道是全双工的可靠信道,UDP 则是不可靠信道。
🚀HTTP 部分
🚁HTTP 长连接和短连接?
- HTTP 1.0 默认使用的是短连接:浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。
- HTTP 长连接,指的是复用 TCP 连接:多个 HTTP 请求可以复用同一个 TCP 连接,这就节省了 TCP 连接建立和断开的消耗。
- HTTP/1.1 起,默认使用长连接:要使用长连接,客户端和服务器的 HTTP 首部的
Connection
都要设置为keep-alive
,才能支持长连接。
🚁HTTP1.1 和 HTTP2.0 的区别?
HTTP2.0 相比HTTP1.1 支持的特性:
- 新的二进制格式:HTTP1.1 基于文本格式传输数据,HTTP2.0 采用二进制格式传输数据,解析更高效。
- 多路复用:在一个连接里,允许同时发送多个请求或响应,并且这些请求或响应能够并行的传输而不被阻塞,避免 HTTP1.1 出现的”队头堵塞”问题。
- 头部压缩:HTTP1.1 的 header 带有大量信息,而且每次都要重复发送。HTTP2.0 把 header 从数据中分离,并封装成头帧和数据帧,使用特定算法压缩头帧,有效减少头信息大小。并且 HTTP2.0 在客户端和服务器端记录了之前发送的键值对,对于相同的数据,不会重复发送。
- 服务端推送:HTTP2.0 允许服务器向客户端推送资源,无需客户端发送请求到服务器获取。
🚁HTTP 的优缺点有哪些?
HTTP 的优点:
- 简单:HTTP 基本的报文格式就是
header + body
,头部信息也是key-value
简单文本的形式,易于理解,降低了学习和使用的门槛。 - 灵活和易于扩展:HTTP 协议里的各类请求方法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。
- 应用广泛和跨平台:HTTP 的应用范围非常的广泛,从台式机的浏览器到手机上的各种 APP,具有跨平台的优越性。
HTTP 的缺点:
- 无状态:服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦。例如,登录->添加购物车->下单->结算->支付,这系列操作都要知道用户的身份才行。但服务器不知道这些请求是有关联的,每次都要问一遍身份信息。
- 明文传输:HTTP 的所有信息都以明文暴露,相当于信息裸奔。在传输的漫长的过程中,信息的内容都毫无隐私可言,很容易就能被窃取。
- 不安全:
(1) 通信使用明文(不加密),内容可能会被窃听
(2) 不验证通信方的身份,因此有可能遭遇伪装
(3) 无法证明报文的完整性,所以有可能已遭篡改
🚁常用的 HTTP 方法有哪些
方法 | 描述 | 是否包含主体 |
---|---|---|
GET | 从服务器获取一份文档 | 否 |
HEAD | 只从服务器获取文档的首部 | 否 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 是 |
TRACE | 对可能经过代理服务器传送到服务器上的报文进行追踪 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
DELETE | 从服务器删除一份文档 | 否 |
🚁HTTP 与 HTTPS 有哪些区别
- HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议
- HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
- HTTP 的端口号是 80,HTTPS 的端口号是 443
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的,需要一定的费用
🚁谈谈 HTTPS 流程
HTTP 由于是明文传输,客户端与服务端通信的信息都是肉眼可见的,通信的内容有被截取的风险,在安全上存在以下风险:
- 窃听风险,例如通信链路上可以获取通信内容。
- 篡改风险,例如强制植入垃圾广告,视觉污染。
- 冒充风险,例如冒充淘宝网站,钓鱼网站。
TLS 协议是解决 HTTP 的风险的方法:
- 信息加密: HTTP 交互信息是被加密的,第三方就无法被窃取。
- 校验机制:校验信息传输过程中是否有被第三方篡改过,如果被篡改过,则会有警告提示。
- 身份证书:证明淘宝是真的淘宝网。
HTTPS 在传输的过程中会涉及到三个密钥:服务器端的公钥和私钥,用来进行非对称加密;客户端生成的随机密钥,用来进行对称加密。一个 HTTPS 请求实际上包含了两次 HTTP 传输,如下图可以细分为以下 8 步:
- 客户端向服务器发起 HTTPS 请求,连接到服务器的 443 端口。
- 服务器端有一个密钥对,即公钥和私钥,是用来进行非对称加密使用的。服务器端保存着私钥,不能将其泄露,公钥可以发送给任何人,服务器将自己的公钥发送给客户端。
- 客户端收到服务器端的公钥之后,会对公钥进行检查,验证其合法性。如果发现发现公钥有问题,那么 HTTPS 传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书的合法性。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密的密钥,我们将该密钥称之为 client key,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS 中的第一次 HTTP 请求结束。
- 客户端会发起 HTTPS 中的第二个 HTTP 请求,将加密之后的客户端密钥发送给服务器。
- 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是客户端密钥,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文,将加密后的密文发送给客户端。
- 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。 这样 HTTPS 中的第二个 HTTP 请求结束,整个 HTTPS 传输完成。
🚁GET 和 POST 相关问题
🪂GET 和 POST 区别
根据 RFC 规范,GET 的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片视频等。GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定)。
根据 RFC 规范,POST 的语义是根据请求负荷(报文body)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body 中, body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。
🪂GET 和 POST 方法都是安全和幂等的吗?
安全和幂等的概念:
- 在 HTTP 协议里,所谓的安全是指请求方法不会破坏服务器上的资源
- 所谓的幂等,意思是多次执行相同的操作,结果都是相同的
(1) GET 方法就是安全且幂等的,因为它是只读操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如 nginx),而且在浏览器中 GET 请求可以保存为书签。
(2) POST 因为是新增或提交数据的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签。 PUT 方法是幂等的。
🚀TCP 部分
🚁TCP 协议如何保证可靠传输?
TCP协议保证数据传输可靠性的方式主要有:
- 校验和
- 序列号
- 确认应答
- 超时重传
- 连接管理
- 流量控制
- 拥塞控制
🚁TCP 三次握手与四次挥手相关问题
🚁TCP 三次握手建立连接过程
三次握手的过程如下图所示:
(1) 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
(2) 客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序列号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT
状态。
(3) 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序列号(server_isn),将此序列号填入 TCP 首部的「序列号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1
, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD
状态。
(4) 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1
,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于 ESTABLISHED
状态。
(5) 服务器收到客户端的应答报文后,也进入 ESTABLISHED
状态。
(6) 一旦完成三次握手,双方都处于 ESTABLISHED
状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。
注意:第三次握手是可以携带数据的,前两次握手是不可以携带数据。
🪂为什么是三次握手而不是两次、四次握手?
(1) 三次握手才可以阻止重复历史连接的初始化
考虑一个场景,客户端先发送了 SYN(seq = 90) 报文,然后客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq = 100) 报文(注意:不是重传 SYN,重传的 SYN 的序列号是一样的)。
下图展示了三次握手是如何阻止历史连接的:
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵情况下:
- 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端,那么此时服务端就会回一个
SYN + ACK
报文给客户端 - 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接
如果是两次握手连接,就无法阻止历史连接。因为在两次握手的情况下,「被动发起方」没有中间状态给「主动发起方」来阻止历史连接,导致「被动发起方」可能建立一个历史连接,造成资源浪费。
(2) 三次握手才可以同步双方的初始序列号
TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:
- 接收方可以去除重复的数据
- 接收方可以根据数据包的序列号按序接收
- 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道)
可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了三次握手。而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。
(3) 三次握手才可以避免资源浪费
如果只有两次握手,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN 报文,而造成重复分配资源
总结:
- TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。
- 两次握手无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号。
- 而对于四次握手,其实三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
🪂第一次握手丢失了,会发生什么?
当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 SYN_SENT
状态。如果第一次握手丢失后,那么客户端就迟迟收不到服务端的 SYN_ACK
报文(第二次握手),就会触发「超时重传」机制,重传 SYN 报文,每次超时重传的时间呈指数倍上涨,直到发送五次之后停止继续发送 SYN 报文。
不同版本的操作系统可能超时时间不同,有 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核。当客户端在 1 秒后没收到服务端的 SYN_ACK
报文后,客户端就会重发 SYN 报文,那到底重发几次呢?
- 在 Linux 里,客户端的 SYN 报文最大重传次数由
tcp_syn_retries
内核参数控制,这个参数是可以自定义的,默认值一般是 5 - 通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍
- 当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接
- 所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右
🪂第二次握手丢失了,会发生什么?
当服务端收到客户端的第一次握手后,就会回复 SYN_ACK
报文给客户端,这个就是第二次握手,此时服务端会进入 SYN_RCVD
状态。
第二次握手的 SYN-ACK 报文其实有两个目的:
- 第二次握手里的 ACK, 是对第一次握手的确认报文
- 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文
由于第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文。
然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。
如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN_ACK 报文。
在 Linux 下,SYN-ACK 报文的最大重传次数由 tcp_synack_retries
内核参数决定,默认值是 5.
因此,当第二次握手丢失了,客户端和服务端都会重传:
- 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由
tcp_syn_retries
内核参数决定 - 服务端会重传
SYN_ACK
报文,也就是第二次握手,最大重传次数由tcp_synack_retries
内核参数决定
🪂第三次握手丢失了,会发生什么?
客户端收到服务端的 SYN_ACK
报文后,就会给服务端回一个 ACK 报文,也就是第三次握手,此时客户端状态进入到 ESTABLISHED
状态。
因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN_ACK
报文,直到收到第三次握手,或者达到最大重传次数。
注意:ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。
🪂为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?
主要有两个原因:
- 为了防止历史报文被下一个相同四元组(源IP地址、目的IP地址、源端口、目的端口)的连接接收(主要原因)
- 为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收
假设每次建立连接,客户端和服务端的初始化序列号都是从 0 开始:
- 客户端和服务端建立一个 TCP 连接,在客户端发送数据包被网络阻塞了,而此时服务端的进程重启了,于是就会发送 RST 报文来断开连接
- 紧接着,客户端又与服务端建立了与上一个连接相同四元组(源IP地址、目的IP地址、源端口、目的端口)的连接
- 在新连接建立完成后,上一个连接中被网络阻塞的数据包正好抵达了服务端,刚好该数据包的序列号正好是在服务端的接收窗口内,所以该数据包会被服务端正常接收,就会造成数据错乱
如果每次建立连接,客户端和服务端的初始化序列号都是一样的话,很容易出现历史报文被下一个相同四元组(源IP地址、目的IP地址、源端口、目的端口)的连接接收的问题。
如果每次建立连接客户端和服务端的初始化序列号都「不一样」,就有大概率因为历史报文的序列号「不在」对方接收窗口,从而很大程度上避免了历史报文,比如下图:
每次初始化序列号不一样能够很大程度上避免历史报文被下一个相同四元组(源IP地址、目的IP地址、源端口、目的端口)的连接接收,但并不是完全避免了(因为序列号会有回绕的问题,所以需要用时间戳的机制来判断历史报文)
🚁TCP 四次挥手断开连接过程
客户端和服务端双方都可以主动断开连接,断开连接后主机中的「资源」将被释放,四次挥手的过程如下图:
(1) 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN
标志位被置为 1 的报文,也即 FIN
报文,之后客户端进入 FIN_WAIT_1
状态。
(2) 服务端收到该报文后,就向客户端发送 ACK
应答报文,接着服务端进入 CLOSED_WAIT
状态。
(3) 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2
状态。
(4) 等待服务端处理完数据后,也向客户端发送 FIN
报文,之后服务端进入 LAST_ACK
状态。
(5) 客户端收到服务端的 FIN 报文后,回一个 ACK
应答报文,之后进入 TIME_WAIT
状态。
(6) 服务器收到了 ACK 应答报文后,就进入了 CLOSED
状态,至此服务端已经完成连接的关闭。
(7) 客户端在经过 2MSL
一段时间后,自动进入 CLOSED
状态,至此客户端也完成连接的关闭。
注意:每个方向都有一个 FIN
和 ACK
,因此通常称为四次挥手。此外,主动关闭连接的才有 TIME_WAIT
状态。
🪂为什么要四次挥手?
回顾下四次挥手双方发 FIN 包的过程:
- 关闭连接时,客户端向服务端发送
FIN
时,仅仅表示客户端不再发送数据了但是还能接收数据。 - 服务器收到客户端的
FIN
报文时,先回一个ACK
应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN
报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK
和 FIN
一般都会分开发送,从而比三次握手导致多了一次。
🪂第一次挥手丢失了,会发生什么?
当客户端(主动关闭方)调用 close
函数后,就会向服务端发送 FIN
报文,试图与服务端断开连接,此时客户端的连接进入到 FIN_WAIT_1
状态。正常情况下,如果能及时收到服务端(被动关闭方)的 ACK
,则会很快变为 FIN_WAIT2
状态。
- 如果第一次挥手丢失了,那么客户端迟迟收不到被动方的
ACK
的话,也就会触发超时重传机制,重传FIN
报文,重发次数由tcp_orphan_retries
参数控制 - 当客户端重传
FIN
报文的次数超过tcp_orphan_retries
后,就不再发送FIN
报文,直接进入到close
状态
🪂第二次挥手丢失了,会发生什么?
当服务端收到客户端的第一次挥手后,就会先回一个 ACK
确认报文,此时服务端的连接进入到 CLOSE_WAIT
状态。ACK 报文是不会重传的,如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN
报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。
- 当客户端收到第二次挥手,也就是收到服务端发送的
ACK
报文后,客户端就会处于FIN_WAIT2
状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的FIN
报文 - 对于
close
函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2
状态不可以持续太久,而tcp_fin_timeout
控制了这个状态下连接的持续时长,默认值是 60 秒 - 这意味着对于调用
close
关闭的连接,如果在 60 秒后还没有收到FIN
报文,客户端(主动关闭方)的连接就会直接关闭 - 如果主动关闭方使用
shutdown
函数关闭连接且指定只关闭发送方向,而接收方向并没有关闭,那么意味着主动关闭方还是可以接收数据的 - 如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于
FIN_WAIT2
状态(tcp_fin_timeout
无法控制shutdown
关闭的连接)
🪂第三次挥手丢失了,会发生什么?
当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN
报文后,内核会自动回复 ACK
,同时连接处于 CLOSE_WAIT
状态,顾名思义,它表示等待应用进程调用 close
函数关闭连接。此时,内核是没有权利替代进程关闭连接,必须由进程主动调用 close
函数来触发服务端发送 FIN
报文。
- 服务端处于
CLOSE_WAIT
状态时,调用了close
函数,内核就会发出FIN
报文,同时连接进入LAST_ACK
状态,等待客户端返回ACK
来确认连接关闭 - 如果迟迟收不到这个
ACK
,服务端就会重发FIN
报文,重发次数仍然由tcp_orphan_retries
参数控制,这与客户端重发FIN
报文的重传次数控制方式是一样的
🪂第四次挥手丢失了,会发生什么?
当客户端收到服务端的第三次挥手的 FIN
报文后,就会回复 ACK
报文,也就是第四次挥手,此时客户端连接进入 TIME_WAIT
状态。
- 在 Linux 系统,
TIME_WAIT
状态会持续2MSL
后才会进入关闭状态 - 然后,服务端(被动关闭方)没有收到
ACK
报文前,还是处于LAST_ACK
状态 - 如果第四次挥手的
ACK
报文没有到达服务端,服务端就会重发FIN
报文,重发次数仍然由前面介绍过的tcp_orphan_retries
参数控制
🪂为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理它的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。
TTL 的值一般是 64,Linux 将 MSL 设置为 30 秒,意味着 Linux 认为数据报文经过 64 个路由器的时间不会超过 30 秒,如果超过了,就认为报文已经消失在网络中了。
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
例如,如果被动关闭方没有收到断开连接的最后的 ACK
报文,就会触发超时重发 FIN
报文,另一方接收到 FIN
后,会重发 ACK
给被动关闭方, 一来一去正好 2 个 MSL. 2 MSL 时长 这其实是相当于至少允许报文丢失一次。比如,若 ACK
在一个 MSL 内丢失,这样被动方重发的 FIN
会在第 2 个 MSL 内到达,TIME_WAIT
状态的连接可以应对。
2MSL 的时间是从客户端接收到 FIN
后发送 ACK
开始计时的。如果在 TIME_WAIT
时间内,因为客户端的 ACK
没有传输到服务端,客户端又接收到了服务端重发的 FIN
报文,那么2MSL 时间将重新计时。
🚁TCP 重传、滑动窗口、流量控制、拥塞控制相关问题
🪂滑动窗口的大小由哪一方决定?
TCP 头里有一个字段叫 window
,也就是窗口大小。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 所以,通常窗口的大小是由接收方的窗口大小来决定的。发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据
🪂接收窗口和发送窗口的大小是相等的吗?
并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。
因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 window
字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
🪂流量控制和拥塞控制的区别
流量控制:如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失,触发重传机制进而导致网络流量的无端浪费。TCP 提供一种机制可以让发送方根据接收方的实际接收能力控制发送的数据量,这就是所谓的流量控制。
拥塞控制:在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大,把这种现象称为网络拥塞。为了避免发送方的数据填满整个网络,TCP 提供了拥塞控制。 拥塞控制的算法:
- 慢开始:TCP 在刚建立连接完成后,首先是有个慢启动的过程,这个慢启动的意思就是一点一点的提高发送数据包的数量,而不是一上来就发大量的数据,这样容易造成网络拥堵的现象。慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,拥塞窗口
cwnd
的大小就会加 1。 - 拥塞避免:当拥塞窗口
cwnd
「超过」慢启动门限ssthresh
就会进入拥塞避免算法,进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd
增加1/cwnd
. - 快重传:使发送方尽早知道发生了个别报文段丢失,并不是出现网络拥塞。要求接受不要登塞自己发送数据时才进行捎带确认,而是立即发送确认,即使收到了失序的报文段也要立即发出对已收到报文段的重复确认。而发送方一旦受到三个连续的重读确认,就将相应的报文段立即重传。
- 快恢复:发送方知道只有个别报文段丢失而不是网络拥塞时,不启动慢开始算法,而是执行快恢复算法,将慢开始门限和拥塞窗口值调整为当前窗口的一半,开始执行拥塞避免算法
两者的区别:
- 流量控制是为了预防拥塞,就像在马路上的行车、交警跟红绿灯是流量控制,点对点通信量的控制
- 当发生拥塞时,如何进行疏散,是拥塞控制,拥塞控制是全局性的,涉及到所有的主机和降低网络性能的因素
🪂常见的重传机制有哪些?
(1) 超时重传
在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。
TCP 会在以下两种情况发生超时重传:
- 数据包丢失
- 确认应答丢失
(2) 快速重传
与超时重传机制以时间驱动不同,快速重传以数据驱动重传。
(3) SACK 方法
(4) Duplicate SACK
🚁什么是对称加密和非对称加密?
- 对称加密:通信双方使用相同的密钥进行加密。特点是加密速度快,但是缺点是密钥泄露会导致密文数据被破解。常见的对称加密有 AES 和 DES 算法。
- 非对称加密:它需要生成两个密钥,公钥和私钥。公钥是公开的,任何人都可以获得,而私钥是私人保管的。公钥负责加密,私钥负责解密;或者私钥负责加密,公钥负责解密。这种加密算法安全性更高,但是计算量相比对称加密大很多,加密和解密都很慢。常见的非对称算法有 RSA 和 DSA.
🚀UDP 部分
🚁如何让 UDP 协议变得可靠?
将传输层 TCP 的可靠传输机制在应用层实现。
- UDP 不是面向连接的协议,由于具有资源消耗小,处理速度快的优点,音频、视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
- 如果想要使用 UDP 还要保证数据的可靠传输,就只能通过应用层来做文章。实现的方式可以参考 TCP 的可靠传输机制,差别就是将 TCP 传输层功能,确认机制、重传机制和窗口确认机制实现在了应用层。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/157049.html