网络编程——TCP和UDP详细讲解
文章目录
- TCP/UDP全面详解
- 什么是TCP和UDP?
- TCP如何保证可靠性?
- 1. 序列号(Sequence Number)
- 2. 确认应答(ACK)
- 3. 超时重传(Timeout Retransmission)
- 4. 窗口控制(Sliding Window)
- 5. 快速重传(Fast Retransmit)
- 三次握手(建立连接)
- 四次挥手(断开连接)
- 为什么是三次握手?为什么是四次挥手?
- 三次握手的原因
- 四次挥手的原因
- TCP和UDP特点
- TCP拥塞控制
- 一、慢启动(Slow Start)
- 举例:
- 二、拥塞避免(Congestion Avoidance)
- 举例:
- 三、超时重传与慢启动重启
- 处理方法:
- 四、快速重传与快速恢复(Fast Retransmit & Fast Recovery)
- 快速重传行为:
- 快速恢复行为:
- 拥塞控制减慢增长速度的条件总结
- 拥塞控制会减慢增长速度的时机:
- TCP/IP 数据链路层的交互过程简述
- TCP 和 UDP 如何区分?
- UDP 中的 `connect()` 函数原理
- `connect()` 的作用:
- TCP连接中的 TIME_WAIT 状态详解
- 一、TIME_WAIT 状态如何产生?
- 二、TIME_WAIT 的两个核心作用
- 1. 确保连接的可靠关闭
- 2. 避免旧连接干扰新连接
- 三、TIME_WAIT 的副作用与优化
- ✅ 解决方法:
- TCP的模型
TCP/UDP全面详解
什么是TCP和UDP?
TCP(Transmission Control Protocol)叫传输控制协议,是面向连接、可靠传输的协议。
UDP(User Datagram Protocol)叫用户数据报协议,是无连接、尽最大努力交付的协议。
一句话总结:
- TCP可靠但慢,像寄快递(要签收、确认收货)
- UDP快速但可能丢失,像发传单(发了就发了,收不收无所谓)
TCP如何保证可靠性?
TCP为了确保数据能安全、完整送到对方,设计了很多机制,主要有:
1. 序列号(Sequence Number)
每发送一段数据,TCP都会打个"编号"。
比如发送第一包数据编号1001,第二包编号2001,接收方按照编号顺序排列,避免了丢包、乱序问题。
2. 确认应答(ACK)
接收方收到数据后,会回一条确认信息(ACK),告诉发送方:“我收到了!下一包从xxxx开始发。”
如果发送方迟迟收不到确认,就会怀疑数据丢了。
3. 超时重传(Timeout Retransmission)
如果一定时间(超时)还没收到确认,发送方就自动重发。
一般超时时间 = 2 × RTT(往返时延)+ 偏差值。
4. 窗口控制(Sliding Window)
为了提高速度,TCP可以同时发多包,不需要一包一确认。
窗口大小定义了一次可以连续发送多少数据而不用等确认。
小例子:
窗口大小=3,发送方可以连续发第1包、第2包、第3包数据,不用等第1包的确认就发第2包!
5. 快速重传(Fast Retransmit)
如果连续收到3个相同的ACK(比如一直提示1001未收到),发送方立刻重发缺失的数据段,而不用等超时。
三次握手(建立连接)
- 客户端发送SYN报文,请求建立连接(SYN=1,seq=J)。
- 服务器收到后,发送SYN+ACK报文,表示接受(SYN=1,ACK=1,seq=K,ack=J+1)。
- 客户端再回一个ACK报文,确认连接建立(ACK=1,ack=K+1)。
三次握手后,双方就可以正式通信了。
特别感谢@frozendure绘制的图片。
四次挥手(断开连接)
- 客户端发FIN报文,请求断开(主动关闭)。
- 服务器收到后,回ACK确认(被动关闭)。
- 服务器数据发完后,也发FIN报文。
- 客户端收到FIN后,回ACK确认,进入TIME_WAIT等待一段时间后彻底关闭。
小图示:
为什么是三次握手?为什么是四次挥手?
三次握手的原因
- 防止陈旧连接(老旧、失效的请求包)误建立连接。
- 确保双方收发能力正常。
如果只两次握手,可能因为网络延迟,服务器误认为客户端还想建立新连接,导致资源浪费。假设两次握手时,A发出的第一个请求连接报文段在某一网络节点长时间滞留,以致延误到连接释放后才到达B。B收到失效的连接请求报文段后,认为是A又发出一次新的连接请求。于是向A发送确认报文段,同意建立连接,此时在假定两次握手的前提下,连接建立成功。这样会导致B的资源白白浪费
假设两次握手时,A发出一个请求报文段,但发送过后A就因为问题而导致下线。之后B收到了A发来的请求连接报文段,给A发送确认报文段,同意建立连接,此时在假定两次握手的前提下,连接建立成功。这样会导致B的资源白白浪费
- 第一次握手(客户端发送SYN):保证客户端能够发送数据。
- 第二次握手(服务器发送SYN-ACK):保证服务器能够接收并发送数据。
- 第三次握手(客户端发送ACK):保证客户端能够接收数据。
四次挥手的原因
- TCP是全双工,发送和接收需要分别关闭。
- 一方关闭发送,不代表另一方立即也关闭发送,因此需要两次FIN和两次ACK,确保双方都完成通信。
TCP和UDP特点
项目 | TCP | UDP |
---|---|---|
是否连接 | 面向连接(需三次握手) | 无连接 |
可靠性 | 可靠,具备确认应答、重传机制 | 不可靠,尽力而为 |
传输速度 | 较慢(因连接建立和状态维护) | 较快(无需连接、开销小) |
传输单位 | 字节流(Stream) | 数据报(Datagram) |
是否顺序 | 保证顺序到达 | 不保证顺序 |
占用资源 | 较多(需维护连接状态) | 较少 |
安全性 | 容易受到SYN洪水攻击等 | 较少攻击面 |
优点 | 可靠性高、顺序到达 | 快速、资源开销小 |
缺点 | 速度慢、资源占用高、易被攻击 | 不可靠、易丢包、乱序 |
适用场景 | 要求高可靠性,能容忍延迟 | 要求实时性强,容忍丢包 |
应用实例 | 文件传输(FTP)、发送邮件(SMTP)、网页浏览(HTTP) | 视频通话(VoIP)、在线视频直播、网络游戏、广播 |
TCP拥塞控制
在 TCP 通信中,如果发送端窗口设置过大,会在短时间内发送大量数据,这可能导致中间路由器和接收端缓冲区被挤爆,进而引发丢包、重传等问题。TCP 拥塞控制是指通过动态调整数据发送速率,避免过多的数据包涌入网络,从而引起网络拥堵甚至网络瘫痪的一种机制。
因此,TCP 引入了拥塞控制窗口(cwnd),并采用如下四种主要算法动态调整窗口大小:
一、慢启动(Slow Start)
- 初始化拥塞窗口 cwnd = 1(以 MSS 为单位)。
- 每收到一个确认应答(ACK),就将 cwnd 翻倍(即指数增长)。
- 每经过一个 RTT,cwnd *= 2。
举例:
- 第一个 RTT:cwnd = 1
- 第二个 RTT:cwnd = 2
- 第三个 RTT:cwnd = 4
- 第四个 RTT:cwnd = 8
✅ 优点:快速提高网络利用率
❌ 缺点:指数增长可能迅速导致拥塞
二、拥塞避免(Congestion Avoidance)
当 cwnd 达到慢启动阈值 ssthresh
(一般初始值为 65536 字节),就不再使用慢启动的指数增长,而是进入拥塞避免阶段。
- 每经过一个 RTT,仅将 cwnd 加 1(线性增长)。
- 防止窗口增长过快引发网络拥塞。
举例:
- cwnd = 64,到达 ssthresh
- 后续每次 ACK,cwnd = cwnd + 1/cwnd ≈ 每 RTT 增加 1
三、超时重传与慢启动重启
如果出现报文段超时未收到 ACK,TCP 认为网络出现拥塞。
处理方法:
- 将
ssthresh
设置为当前 cwnd 的一半; - 将 cwnd 设为 1;
- 重新进入慢启动阶段。
🔁 这是 TCP 的自我修复机制,避免持续拥塞。
四、快速重传与快速恢复(Fast Retransmit & Fast Recovery)
如果收到 3 个重复的 ACK(即 ACK 重复了 3 次),TCP 认为某个报文段丢失。
快速重传行为:
- 立即重传丢失的段,而不是等待超时。
快速恢复行为:
- ssthresh = 当前 cwnd / 2;
- cwnd = ssthresh + 3(体现网络尚可);
- 进入拥塞避免阶段(不再重回慢启动);
✅ 快速恢复保持了中等的发送速率,防止性能大幅下降。
拥塞控制减慢增长速度的条件总结
拥塞控制会减慢增长速度的时机:
- cwnd 超过 ssthresh:
- 进入拥塞避免阶段,从指数增长变为线性增长。
- 发生超时重传:
- 表明网络严重拥塞,立即进入慢启动(cwnd 重置为 1)。
- 收到 3 个重复 ACK:
- 进入快速重传,cwmd 缩减并进入拥塞避免。
TCP/IP 数据链路层的交互过程简述
当 IP 层向数据链路层(如以太网)传递数据时:
- 查找 ARP 缓存:查找目标 IP 对应的 MAC 地址;
- 若存在,直接封装 MAC 地址;
- 若不存在,发起 ARP 广播请求;
- 接收到 IP 匹配的设备返回自己的 MAC 地址;
- 使用该 MAC 地址封装链路层帧,完成数据发送。
TCP 和 UDP 如何区分?
传输层协议由 IP 协议头中的 Protocol 字段决定:
- TCP:值为 6
- UDP:值为 17
操作系统根据协议和端口号,将报文传递给对应的应用程序。
UDP 中的 connect()
函数原理
connect()
的作用:
- 记录对端 IP 与端口号;
- 发送数据时不再需要 sendto() 指定地址,可以使用 write()、send();
- 接收数据时只接受来自该对端的数据;
- 可接收异步错误信息;
- 丢弃来自其他地址的包。
✅ 用于长期通信(如 TFTP)或希望使用 TCP 接口风格的场景。
TCP连接中的 TIME_WAIT 状态详解
一、TIME_WAIT 状态如何产生?
- 主动关闭连接的一方(client)发送最后一个 ACK 后进入
TIME_WAIT
; - 保持 2 * MSL 时间后释放连接;
- MSL(Maximum Segment Lifetime):报文段在网络中的最大生存时间。
二、TIME_WAIT 的两个核心作用
1. 确保连接的可靠关闭
- 若最后一个 ACK 丢失,server 会重发 FIN;
- client 能在
TIME_WAIT
状态中接收该 FIN,并再次回复 ACK; - 保证被动关闭方正常结束连接。
2. 避免旧连接干扰新连接
- 如果不等待,可能使用相同四元组快速建立新连接;
- 网络中仍存在旧连接的数据包,可能被误接收,导致数据错乱;
- 2MSL 等待时间保证旧数据失效。
三、TIME_WAIT 的副作用与优化
- 占用端口资源;
- 影响服务器重启;
✅ 解决方法:
设置套接字选项 SO_REUSEADDR
,允许 TIME_WAIT 状态的端口被重复绑定。
TCP的模型
四层TCP/IP模型如下,包括应用层、网络层、数据链路层、物理层
特别感谢@frozendure绘制的图片和说明。