传输层
约 1502 字大约 5 分钟
2024-10-23
传输层
TCP 和 UDP 的核心区别是什么?它们分别适用于哪些应用场景?
| TCP | UDP |
|---|---|
| 面向连接 | 无连接 |
| 提供可靠服务 | 尽最大努力,不保证可靠交付 |
| 面向字节流 | 面向报文 |
| 只能点到点 | 支持一对一,一对多,多对一和多对多 |
| 首部开销 20 字节 | 首部开销 8 个字节 |
| 可靠逻辑通信信道(全双工) | 不可靠逻辑通信信道 |
| 适用于不能容忍数据丢失或出错的场景(网页、文件传输、邮件发送) | 适用于速度时效性要求高,容许少量数据丢失的场景(直播、网络游戏) |
参考:- TCP与UDP的区别
TCP 有哪些核心特性?
- 面向连接 (Connection-oriented): 在发送任何数据之前,通信双方必须首先建立一个连接。
- 可靠性 (Reliability): 保证数据无差错、不丢失、不重复,且按序到达。如果数据包在网络中丢失,TCP 会负责重新发送。
- 面向字节流 (Byte-stream oriented): 应用程序交给 TCP 的数据被看作是一连串无结构的字节流。TCP 会根据当前网络状况将其拆分成合适大小的报文段(Segment)进行发送。
- 全双工通信 (Full-duplex): 允许通信双方在任何时候都能同时发送和接收数据。
TCP 是如何保证可靠传输的?
- 序列号与确认应答 (ACK): 每个发出的字节都有一个序号。接收端收到数据后会回复一个确认号(ACK),告诉发送端“这个序号之前的数据我都收到了”。
- 超时重传: 如果发送端发出数据后,在规定时间内没有收到对应的 ACK,就会认为数据丢失,并自动重新发送该数据包。
- 流量控制 (Flow Control): TCP 使用滑动窗口 (Sliding Window) 机制。接收端会告诉发送端自己缓冲区的剩余大小,发送端会根据这个大小控制发送速度,防止接收端处理不过来而导致数据丢失。
- 拥塞控制 (Congestion Control): 与流量控制关注“端到端”不同,拥塞控制关注整个“网络”。当网络出现拥堵时(表现为大量丢包),TCP 会主动降低发送速度,机制包括慢启动(Slow Start)、拥塞避免(Congestion Avoidance)、快重传和快恢复。
TCP 的“三次握手”和“四次挥手”过程
三次握手 (建立连接):
- 第一次握手 (SYN): 客户端向服务器发送一个 SYN(同步序列号)报文,表示想要建立连接,并进入 SYN_SENT 状态。
- 第二次握手 (SYN + ACK): 服务器收到 SYN 后,需要确认客户端的请求(ACK),同时自己也发送一个 SYN 包(SYN+ACK),此时服务器进入 SYN_RCVD 状态。
- 第三次握手 (ACK): 客户端收到服务器的 SYN+ACK 后,向服务器发送确认包 (ACK)。发送完毕后,双方进入 ESTABLISHED(已建立连接)状态,可以开始传输数据。
四次挥手 (断开连接):
- 第一次挥手 (FIN): 客户端(或服务端)发送一个 FIN 报文,告诉对方自己没有数据要发送了。
- 第二次挥手 (ACK): 对方收到 FIN 报文后,回复一个 ACK 确认包。此时连接处于半关闭状态(发送方不再发送数据,但仍可接收)。
- 第三次挥手 (FIN): 当被动关闭方也没有数据要发送时,它也会发送一个 FIN 报文。
- 第四次挥手 (ACK): 主动关闭方收到 FIN 后,回复一个 ACK 确认包。经过一段等待时间(TIME_WAIT)后,连接彻底关闭。
为什么建立连接是三次握手,而断开连接需要四次?
建立连接时,服务端的“确认”和“请求”可以打包在一起发。所以三次就够。
关闭连接时,由于 TCP 是全双工的,客户端向服务器发起关闭请求时,服务器可能还有数据没发送完毕,因此只能先“确认”,随后等数据全部发送完毕了,再单独关闭。这个过程必须要四次交互。
在实际网络传输中,当被动关闭方(收到 FIN 的一方)在收到断开请求时,如果此时它的缓冲区里已经没有任何需要发送的数据,并且应用程序也立刻调用了关闭连接的指令,TCP 协议栈就会进行优化,将原本需要分两步发送的包合并。这就是所谓的 捎带确认(Piggybacking) 机制。在这个场景下,四次挥手就被压缩成了三次。
什么是 TIME_WAIT 状态?为什么需要这个状态?
TIME_WAIT 是 TCP 连接在彻底关闭前,主动发起关闭的一方必须经历的一个“等待期”,通常是 2 MSL (Maximum Segment Lifetime,报文最大生存时间),也就是 60 秒。
为什么需要这个状态?
- 可靠关闭:设置这个状态的目的在于 TCP 需要可靠地关闭连接,保证最后的 ACK 能成功到达。
- 防止数据串扰:如果连接一断开端口就立刻被用来建立一个全新的连接,没有 TIME_WAIT 机制的话,上一个连接的被动关闭方如果发生了重传,就可能发生数据串扰(旧链接干扰新链接)。
如果系统中出现大量 TIME_WAIT 会有什么影响,如何解决?
主要影响是 端口耗尽 和 系统资源占用。
应用层解决办法:
- 使用长连接 (Keep-Alive):例如在 HTTP 中使用
Connection: Keep-Alive,在数据库或redis中使用连接池,避免频繁地建立和断开连接。
操作系统参数解决办法(Linux):
- 开启端口复用:在
/etc/sysctl.conf中设置net.ipv4.tcp_tw_reuse = 1,这个参数允许系统在安全的情况下,将处于 TIME_WAIT 状态的端口直接拿来用于新的对外连接 - 扩大本地可用端口范围:
net.ipv4.ip_local_port_range = 1024 65535
不要开启 tcp_tw_recycle。 > 在网上很多旧教程中会推荐开启 net.ipv4.tcp_tw_recycle 来快速回收连接。但在现代网络(尤其是存在 NAT 或负载均衡的环境下),开启它会导致严重的丢包和连接失败问题。Linux 内核在 4.12 版本中已经彻底废弃并移除了这个参数。
