计网知识收集

基础知识

1.TCP 三次握手的过程

第一次握手(SYN):

  • 动作: 客户端随机生成一个初始序列号 seq = x,设置 SYN 标志位为 1,发送给服务器。
  • 状态转移: 客户端进入 SYN_SENT(同步已发送)状态。

第二次握手(SYN + ACK):

  • 动作: 服务器收到 SYN 包后,也随机生成自己的初始序列号 seq = y,同时确认客户端的序列号 ack = x + 1,设置 SYN 和 ACK 标志位均为 1,发送给客户端。
  • 状态转移: 服务器从 LISTEN 进入 SYN_RCVD(同步收到)状态。

第三次握手(ACK):

  • 动作: 客户端收到服务器的 SYN+ACK 包,为了确认自己收到了服务器的序列号,发送一个 ack = y + 1,序列号 seq = x + 1,ACK 标志位为 1 的确认包给服务器。(此时,这个包已经可以携带应用层数据了)
  • 状态转移: 客户端进入 ESTABLISHED(已建立连接)状态。服务器收到此 ACK 后,也进入 ESTABLISHED 状态。

为什么需要三次握手?

  1. 防止历史(失效)连接初始化造成混乱(最主要原因): 网络是可能拥塞和延迟的。假设客户端发送了 SYN1(因网络卡顿未到达),于是超时重发了 SYN2。此时网络恢复,旧的 SYN1 先到达服务器。
    • 如果是两次握手:服务器收到 SYN1 直接进入 ESTABLISHED 状态并开始发数据,这就建立了一个历史的“脏连接”,浪费了服务器资源。
    • 如果是三次握手:服务器收到 SYN1 返回 SYN+ACK,客户端收到后,检查发现期望的应答不是这个(因为它当前期望的是 SYN2 的应答),客户端就会发送 RST 报文强行中断这个历史连接。三次握手把决定权交给了客户端,让客户端来确认这个连接是不是它真正想要的。
  2. 可靠地同步双方的初始序列号(ISN): TCP 要保证数据不丢不重,必须靠序列号。双方都要告诉对方自己的 ISN。
    • 客户端发 ISN,服务器确认(1、2次握手完成)。
    • 服务器发 ISN,客户端确认(2、3次握手完成)。 最少需要这三次通信,才能保证双方的序列号都被对方可靠地接收并确认。

第三次握手(ACK)失败会发生什么?

这在面试中是极其考验实战经验的问题。

如果客户端发送了第三次 ACK,但网络丢包了,服务器没收到:

  • 客户端视角: 客户端发完 ACK 后就认为连接成功了,状态已经是 ESTABLISHED。如果它开始发送数据包,服务器收到后会回复 RST 报文让其重置连接。
  • 服务器视角: 服务器此时处于 SYN_RCVD 状态。因为它没收到 ACK,它会认为自己的 SYN+ACK 丢了。于是它会触发超时重传机制,重新发送 SYN+ACK。
    • 通常会重传 5 次,并且每次等待时间呈指数级增长(如 1s, 2s, 4s, 8s, 16s)。
    • 如果在最大重传次数后依然等不到客户端的 ACK,服务器就会将这个半连接从**半连接队列(SYN Queue)**中删除,释放资源。

2.TCP 挥手为什么需要四次?

这里的核心关键字是:全双工通信(Full-Duplex)半关闭状态(Half-Close)

TCP 连接是双向的(就像一条双向车道),数据的发送和接收是相互独立的。这就意味着,关闭连接也必须分两个方向独立进行

  1. 第一次挥手(FIN): 客户端数据发完了,告诉服务器:“我没有数据要发了”(发送 FIN)。注意:此时客户端只是不能发数据了,但依然可以收数据。
  2. 第二次挥手(ACK): 服务器收到 FIN,马上回复一个 ACK:“我知道你发完了”。此时客户端到服务器的方向关闭,连接处于半关闭状态
  3. 为什么这里有停顿? 因为服务器收到 FIN 的时候,它的应用层可能还有数据没处理完,或者还有数据没发送完! 所以服务器还要继续把剩下的数据发给客户端。客户端也要继续接收。
  4. 第三次挥手(FIN): 等服务器的数据也终于全部发完了,它也会发一个 FIN 告诉客户端:“我也没数据要发了,可以正式分手了”。
  5. 第四次挥手(ACK): 客户端收到服务器的 FIN,回复一个 ACK:“好的,再见”。

简单来说:建立连接时,服务器的 SYN 和 ACK 可以合并成一个包发送(第二次握手)。但断开连接时,由于被动方往往还有残余数据要发送,所以它的 ACK(第二次挥手)和 FIN(第三次挥手)必须分开发送,这就导致了必须是四次挥手。

3.描述一下https中的SSL/TLS

在深入细节前,我们要明确 SSL/TLS 解决了 HTTPS 的三个核心问题:

  • 机密性 (Confidentiality):通过对称加密防止信息被窃听。
  • 完整性 (Integrity):通过**MAC(消息验证码)**防止数据被篡改。
  • 身份认证 (Authentication):通过数字证书确保你访问的确实是目标服务器。

这是最体现底层设计精妙的地方。TLS 握手的本质是:在不安全的信道上,安全地协商出一个只有双方知道的对称密钥。

阶段一:密码套件协商 (Client Hello & Server Hello)

  1. Client Hello: 客户端发送它支持的 TLS 版本、密码套件列表(如 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)以及一个客户端随机数 (Client Random)
  2. Server Hello: 服务端选定版本、密码套件,并给出服务端随机数 (Server Random)

阶段二:身份验证与密钥交换

这是区分资深开发者的关键点,通常涉及 ECDHE 算法:

  1. Certificate: 服务端发送证书链。客户端会根据本地根证书校验签名,确保服务器公钥的真实性。
  2. Server Key Exchange: 如果使用 ECDHE,服务端会生成一个椭圆曲线点(临时公钥)并用私钥签名发送给客户端。
  3. Client Key Exchange: 客户端验证签名后,也生成一个自己的临时公钥发送给服务端。

阶段三:生成会话密钥

此时,双方都拥有:Client RandomServer Random 以及通过 ECDHE 算法算出的 Pre-Master Secret

  • 双方利用这三个参数,通过 PRF (伪随机函数) 计算出最终的 Master Secret (主密钥)
  • 主密钥再派生出对称加密密钥、MAC 密钥等。

深度思考:为什么要用三个随机数?因为 SSL 不信任客户端或服务端伪随机数的独立性,三个随机数混合可以极大增加密钥的随机性,确保安全性。

握手完成后,所有应用数据(如 HTTP Body)都通过记录协议处理。

  • 分片 (Fragmentation):将高层协议的数据切分成适当大小。
  • 压缩 (Compression):可选,但现代 TLS 为了防范攻击(如 CRIME)通常禁用。
  • 添加 MAC (Message Authentication Code):通过哈希算法(如 SHA-256)保证数据完整性。
  • 加密 (Encryption):使用协商好的对称密钥(如 AES-GCM)进行加密。

传统的 RSA 密钥交换模式下,如果服务器私钥泄露,过去录制的所有加密流量都可以被解密。 而 ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) 算法生成的临时密钥对是“瞬时”的。即使服务器私钥泄露,攻击者也无法推算出之前的 Pre-Master Secret。这就是前向安全性,也是为什么现代互联网强制要求使用 ECDHE 的原因。

4.描述一下TIME_WAIT状态,它出现在tcp链接的哪个位置

TIME_WAIT 仅出现在 主动关闭连接的一方(Active Closer)。

当一方完成数据传输,发送 FIN 包并经历完整的四次挥手流程后,它不会直接进入 CLOSED 状态,而是停留在 TIME_WAIT 状态。

四次挥手与状态流转

  1. 主动方发送 FIN,进入 FIN_WAIT_1
  2. 被动方回复 ACK,进入 CLOSE_WAIT;主动方接收后进入 FIN_WAIT_2
  3. 被动方发送 FIN,进入 LAST_ACK
  4. 主动方接收 FIN 并发送最后的 ACK,此时主动方即刻进入 TIME_WAIT 状态。

TIME_WAIT 的持续时间通常是 2MSL(Maximum Segment Lifetime,报文最大生存时间的 2 倍,Linux 下通常为 60 秒)。设计这个“漫长”等待期的原因有二:

A. 保证最后的 ACK 能到达对端

如果主动方发送的最后一个 ACK 在网络中丢失,被动方(处于 LAST_ACK 状态)会触发超时重传 FIN

  • 如果主动方没有 TIME_WAIT 而是直接关闭,当它再次收到这个重传的 FIN 时,会回复 RST
  • 有了 TIME_WAIT,主动方可以重新发送 ACK,确保被动方能够正常进入 CLOSED 状态。

B. 防止“旧报文”干扰新连接

网络路径复杂,某些报文可能会在路由器中“迷路”后又重新出现。

  • 如果没有 TIME_WAIT,且立刻建立了一个相同 五元组(源IP、源端口、目的IP、目的端口、协议)的新连接。
  • 旧连接的迟到报文可能被新连接接收,导致逻辑错误或数据损坏。
  • 2MSL 确保了旧连接产生的所有报文都在网络中自然消失。

5… HTTP 请求头里面有些什么

  1. 核心路由与连接管理 (Routing & Connection)

这是 HTTP 请求能够成功到达目标服务器并维持通信的基础。

  • Host(极其重要) 指定请求的目标域名和端口号。在单台服务器部署多个网站(虚拟主机)时,Nginx 就是依靠 Host 头来决定把请求路由给哪个后端服务的。
  • Connection:控制网络连接的状态。在 HTTP/1.1 中默认是 keep-alive(长连接),这意味着 TCP 连接在发送完响应后不会立即关闭,以便复用,从而降低握手开销。
  1. 内容协商 (Content Negotiation)

客户端通过这类请求头告诉服务端:“我能接收什么样的数据”。

  • Accept:客户端期望接收的数据格式。例如 application/jsontext/html。如果 Spring Boot 接口返回 XML,而客户端 Accept 只写了 application/json,就会报 406 Not Acceptable 异常。
  • Accept-Encoding:客户端支持的压缩算法,如 gzip, deflate, br (Brotli)。服务端据此对响应体进行压缩,大幅节省带宽。
  • Accept-Language:客户端期望的自然语言,常用于国际化 (i18n) 路由。
  1. 请求体描述 (Entity Information)

当请求带有 Body 时(如 POST/PUT),需要告诉服务端 Body 的特征。

  • Content-Type:请求体的 MIME 类型。最常见的是:
    • application/json:现代 RESTful API 的标配。
    • application/x-www-form-urlencoded:传统的表单提交。
    • multipart/form-data:用于文件上传。
  • Content-Length:请求体的字节长度。如果服务端收到的数据少于这个值,会等待;如果多于这个值,会截断。
  1. 身份认证与状态维持 (Auth & State)

HTTP 协议本身是无状态的,依靠这些头来实现“记住你是谁”。

  • Authorization:携带认证凭证。在微服务和前后端分离架构中,最常见的是 Bearer <JWT_TOKEN>
  • Cookie:携带服务端之前颁发并存储在客户端的 Cookie 键值对,传统的 Session 机制就是依靠传递 JSESSIONID 的 Cookie 来维持状态的。
  1. 缓存控制 (Cache Control) - 面试高频区

优秀的缓存设计能扛下极其夸张的并发,这些头是前端与服务端协商缓存的核心。

  • Cache-Control:控制缓存的策略(如 no-cache, max-age=3600)。
  • 协商缓存双雄
    • If-Modified-Since:配合服务端的 Last-Modified 使用,询问“在这个时间点之后,文件修改过吗?”。
    • If-None-Match:配合服务端的 ETag(文件内容的哈希值)使用,询问“这个文件的指纹变了吗?”。没变的话服务端直接返回 304 Not Modified。
  1. 来源追踪与安全防护 (Tracking & Security)
  • User-Agent (UA):发起请求的客户端信息(浏览器版本、操作系统等)。常用于基础的埋点统计、PC/移动端适配以及初级的反爬虫策略。
  • Referer:记录当前请求是从哪个页面的链接跳过来的。我们常利用这个头在 Nginx 层做防盗链,或者在网关层做 CSRF(跨站请求伪造)防护。
  1. 分布式与微服务生态(资深必谈的“自定义头”)

在现代微服务架构中,请求往往要经过 CDN、WAF、API 网关等层层代理,原生头信息可能会丢失,因此我们极其依赖以下扩展头:

  • X-Forwarded-For / X-Real-IP:用于在经过多层反向代理后,透传客户端的真实物理 IP,否则你服务里取到的永远是网关的 IP。
  • Trace-Id / Span-Id:在分布式链路追踪系统(如 SkyWalking、Zipkin)中,我们会在网关处生成一个全链路唯一的 ID 放入请求头,后续的所有微服务调用(哪怕是 Feign 或 RestTemplate)都会携带这个头,借此把整个请求日志串联起来。