0%

面经总结之TCP

最近换工作,面试发现很多公司都爱问 TCP 协议,所以根据自己的理解总结了一份手记仅供面试使用。

TCP 是一种什么协议

TCP 是一种建立在 IP 协议上的面向连接、可靠、基于流的传输协议

TCP 三次握手流程

  1. 客户端生成 ISN,SYN 到服务器
  2. 服务器生成 ISN,SYN 和 ACK 到客户端
  3. 客户端 ACK 到服务器,可以携带数据

TCP 的三次握手原因

  1. 确认双方收发能力
  2. 同步双方的序列号和窗口大小,用于后续的数据传输可靠性和有序性
  3. 阻止重复的历史连接,如果旧的 SYN 比新的 SYN 先到,客户端在第三次握手的时候就通过 seq 可以知道是旧的 SYN,可以直接发 RST 来终止连接 (解释为什么不是两次)

TCP 四次挥手流程

  1. 客户端发送 FIN FIN_WAIT1
  2. 服务端发送 ACK(服务端不再接收新的数据) TIME_WAIT
  3. 客户端收到 ACK(客户端不再发送新的数据) FIN_WAIT2
  4. 服务端处理完数据发送 FIN
  5. 客户端 ACK CLOSE_WAIT
  6. 服务端接收到 ACK 后关闭连接 CLOSED
  7. 客户端发送完 ACK 之后等待 2MSL 关闭连接 CLOSED

MSL 是什么

MSL 是指报文在网络上的最大生存时间,超过这个时间一般就可以认为报文已经丢失,linux 上是 30 秒,2MSL 就是 60 秒

为什么等 2MSL

因为考虑到数据传输需要一来一回,所以要等待 2MSL

TIME_WAIT 过多有什么危害

服务器发起的主动关闭,导致 FD 不够用,可能无法接受新的连接
客户端发起的主动关闭,导致端口不够用,可能无法发起新的连接

TCP 有序性怎么保证

通过双方约定的 seq 保证

TCP 可靠性怎么保证

通过重传机制保证

TCP 重传

在报文一直没有被 ACK 时,就会触发超时重传,超时时间有一个复杂的公式来动态计算,如果再超时则等待时间翻倍,一直到超时重传限制数,如果任然没有应答说明网络或对方有异常,关闭连接。
超时重传次限制数为 15

快速重传

接受到三次相同 seq 的 ACK,就会触发快速重传

SACK

在 ACK 的时候告诉发送方已接受到的 seq,这样发送就知道需要重传哪些数据

D-SACK

在 ACK 的时候告诉发送放已接受到的 seq,通知对方不需要重传了已经接受到了,只是之前没 ack 到

滑动窗口和流量控制

接收方通过窗口大小告诉发送方自己能处理的报文数,
为了解决 TCP 每次发送数据都要一答一应的低效问题,滑动窗口为了解决这个问题,设计了可以在请求没被 ACK 的情况下继续发送数据,双方约定各自的窗口大小,这个大小就是无需 ACK 就可以发送数据的大小。

窗口大小由哪一方决定

窗口大小是接收方告诉发送方自己的缓冲区还有多少数据可以接收,所以窗口大小都是由接收方决定

窗口满了怎么处理

发送方会定时去检测,如果检测到接收方窗口大小有空闲了就继续发送。

拥塞控制

流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。而拥塞控制是为了避免网络出现拥堵时继续发送大量的数据包,导致包延时、丢失等等触发重传进而加大网络的拥堵程度导致网络瘫痪。
拥塞窗口(cwnd)是发送端维护的,和滑动窗口中的接收窗口关联,在发送时在两者中取最小作为发送窗口大小,一个 cwnd 表示可以传 1MSS(1460)大小的数据

慢启动

每次 ack,cwnd+1,指数级增长,直到慢启动阀值(ssthresh=65535):1>2,2>4,4>8
拥塞避免算法:
超过阀值之后每次 ack,cwnd+1/cwnd,线性增长

拥塞发生

发生超时重传时,cwnd 重置为 1,慢启动阀值(ssthresh)设置为当前 cwnd/2,接着又开始慢启动流程
发生快速重传时,TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,慢启动阀值(ssthresh)设置为当前 cwnd/2,然后进入快速恢复

快速恢复算法

  1. cwnd=慢启动阀值(ssthresh)+3(快速重传的 3 次 ack)
  2. 后续如果还有收到重复的 ack,则 cwnd+1
  3. 收到新的 ack 时快恢复结束,将 cwnd 的值设置为慢启动阀值(ssthresh),重新进入拥塞避免阶段

我是MonkeyWie,欢迎扫码👇👇关注!不定期在公众号中分享JAVAGolang前端dockerk8s等干货知识。

如果觉得本文对您有帮助,可以请我喝一杯咖啡☕