Administrator
发布于 2025-11-12 / 7 阅读
0
0

TCP/IP协议栈

工作之余,复盘下TCP/IP协议栈。目的为更深层次理解其设计哲学、关键机制以及开发生涯中可能被“黑盒化”的细节。

首先明确下,TCP/IP不是一个协议,而是一个协议族,它的核心是分层设计。每一层只关心自己层的事,通过标准的接口为上层提供服务。

  1. 应用层:HTTP、FTP、SMTP、DNS等

  2. 传输层:TCP、UDP

  3. 网络层:IP、ICMP

  4. 网络接口层:Ethernet、WIFI

数据“流动”过程为:你的HTTP数据----->被加上TCP投----->被加上IP头----->被加上以太网头/MAC地址----->变成比特流----->仍到网络上。

应用层协议点睛

  • HTTP/1.1:持久连接、管道化(但存在队头阻塞)。

  • HTTP/2:二进制分帧、多路复用(解决队头阻塞)、头部压缩、服务器推送。

  • HTTPS = HTTP + TLS/SSL。TLS握手(非对称加密交换密钥) -> 对称加密通信。

  • DNS:UDP协议,端口53。递归查询 vs 迭代查询。DNS缓存是性能关键。

传输层核心:TCP和UDP

这是老生常谈,但我们深入一下。

UDP协议:

UDP属极简主义(关键数据结构仅包含:源端口、目标端口、长度、校验和),主张无连接、不可靠、尽最大努力交付。“不可靠”是特性,不是BUG,他把复杂性交给了应用层,换来了极低的延迟和开销。

TCP协议:

TCP属完美主义,TCP的核心是在不可靠的IP层之上,建立一个可靠的、面向连接的、字节流的通道。

  1. 三次握手 (Three-way Handshake) - 建立连接

(为什么是三次,不是两次?)

核心目的:确认双方的收发能力都正常,并同步初始序列号 (ISN)。防止已失效的连接请求报文:如果是两次握手,一个延迟的SYN包可能会让Server单方面建立连接,造成资源浪费。

过程:

Client -> Server: SYN=1, seq=X (Client说:“我能发,我的序列号从X开始。”)

Server -> Client: SYN=1, ACK=1, seq=Y, ack=X+1 (Server说:“我能收也能发。我收到了你的X,期待你下一个发X+1。我的序列号从Y开始。”)

Client -> Server: ACK=1, seq=X+1, ack=Y+1 (Client说:“好的,我收到了你的Y。这是我们第一次有效数据传输,我发的是X+1。”)

  1. 四次挥手 (Four-way Wavehand) - 断开连接

为什么是四次?

核心原因:TCP连接是全双工的,每一方向必须单独关闭。

过程:

Client -> Server: FIN=1, seq=u (Client说:“我数据发完了,要关闭我到你方向的连接。”)

Server -> Client: ACK=1, seq=v, ack=u+1 (Server说:“收到你的FIN了。”) -> 此时进入CLOSE_WAIT状态,Server可能还有数据要发给Client。

(Server处理完剩余数据) Server -> Client: FIN=1, ACK=1, seq=w, ack=u+1 (Server说:“我这边数据也发完了,我也要关了。”)

Client -> Server: ACK=1, seq=u+1, ack=w+1 (Client说:“收到,再见。”) -> Client进入TIME_WAIT状态。

  1. 关键机制

  • 序列号与确认应答 (ACK):每个字节都有编号。接收方通过ACK告知发送方“我期望收到的下一个字节的序列号”,这是累积确认。

  • 超时重传:发送一个数据段后启动定时器,如果超时未收到ACK,则重传。

  • 流量控制 (Flow Control) - 解决接收方处理不过来的问题

通过 滑动窗口 (Sliding Window) 和 接收窗口 (rwnd) 实现。

接收方在ACK包中通告自己的接收窗口大小,表示自己还能处理多少数据。发送方发送的数据不能超过这个窗口。

  • 拥塞控制 (Congestion Control) - 解决网络处理不过来(拥堵)的问题

发送方维护一个 拥塞窗口 (cwnd)。实际发送窗口 = min(rwnd, cwnd)。

核心算法:

慢启动 (Slow Start):连接开始时,cwnd指数增长(每收到一个ACK,cwnd+1)。

拥塞避免 (Congestion Avoidance):当cwnd超过慢启动阈值(ssthresh)后,转为线性增长(每RTT时间,cwnd+1)。

拥塞发生:

超时:认为网络非常拥堵。ssthresh降为cwnd/2,cwnd重置为1,重新慢启动。

收到3个重复ACK (Fast Retransmit):认为只是丢了个别包。ssthresh = cwnd/2, cwnd = ssthresh + 3,然后进入快速恢复 (Fast Recovery),线性增长,收到新数据的ACK后退出。

4. TIME_WAIT状态

为什么需要TIME_WAIT? 持续时间通常是2MSL (Maximum Segment Lifetime)。

可靠地终止连接:确保最后的ACK能到达对方。如果ACK丢失,对方会重发FIN,你还在TIME_WAIT,可以重发ACK。

让旧连接的报文在网络中消逝:防止具有相同四元组(源IP、源端口、目标IP、目标端口)的旧连接数据包干扰新连接。

实际问题:高并发短连接服务器上,大量连接处于TIME_WAIT状态,可能导致端口耗尽。解决方案包括开启 SO_REUSEADDR 套接字选项,允许内核重用处于TIME_WAIT状态的连接的端口。

网络层核心:IP协议

IP是无连接的:发送前不需要握手,每个数据包独立路由。

MTU (Maximum Transmission Unit):数据链路层能承载的最大数据量。超过MTU的IP数据包需要分片 (Fragmentation)。PMTUD (Path MTU Discovery) 机制用于发现路径上的最小MTU,避免分片(因为分片重组消耗资源,且任何一片丢失整个IP包作废)。

NAT (Network Address Translation):解决IPv4地址枯竭的核心技术。内网IP通过一个公网IP出口。

NAT打洞:P2P应用的基石。通过一个公共服务器协调,让两个都在NAT后的主机建立直接连接。


评论