4. 传输层协议详解:TCP与UDP
传输层,说白了就是端到端通信的"快递公司"。它负责把数据从一台机器的某个程序,准确送到另一台机器的某个程序。我刚开始学网络时,总觉得IP层搞定一切就够了,后来被丢包坑了几次才明白——没有传输层,应用层根本没法好好聊天。
这一章我们重点啃TCP和UDP。TCP像顺丰,可靠但慢;UDP像发语音,快但可能听不清。嗯,咱们一个一个来。
4.1 TCP协议:可靠的"三次握手"
TCP是面向连接的协议。什么叫面向连接?就是正式传数据之前,双方得先"握个手",确认对方在线、愿意聊、能收数据。我见过不少新手直接跳过握手就开始发数据,结果程序跑起来各种诡异超时。
三次握手的过程,说白了就是三次对话:
- 客户端发SYN:嘿,在吗?我想跟你说话。(同步序列号)
- 服务端回SYN+ACK:在呢,收到。我也想说,你准备好了吗?
- 客户端发ACK:准备好了,开始吧!
为什么非得三次?两次行不行?你想想看,如果只有两次握手,服务端没法确认客户端收到了自己的SYN+ACK。万一这个包丢了,服务端以为连接建立了,客户端却不知道,两边状态就不一致了。我早期做嵌入式网络时就被这个坑过——服务端傻等数据,客户端傻等确认,死锁了。
4.2 TCP的"四次挥手"
断开连接比建立连接多一次,因为TCP是全双工的——两边都能发也能收。所以断开得两边各自说再见。
| 步骤 | 发送方 | 报文 | 含义 |
|---|---|---|---|
| 1 | 主动方 | FIN | 我发完了,准备关闭 |
| 2 | 被动方 | ACK | 收到,但我还有数据要发 |
| 3 | 被动方 | FIN | 我也发完了,可以关了 |
| 4 | 主动方 | ACK | 收到,关闭 |
4.3 滑动窗口:流量控制的核心
滑动窗口是TCP用来控制发送速度的机制。说白了,就是接收方告诉发送方:"我一次能收多少,你别发太快。"
窗口大小是动态调整的。接收方处理不过来时,会缩小窗口;处理完了再扩大。我见过一个案例:某视频监控平台,摄像头端窗口一直为0,导致视频流卡死。查了半天,发现是接收端应用层没及时读取缓冲区。
- 发送窗口 = min(拥塞窗口, 接收窗口)
- 窗口为0时,发送方停止发送,启动"坚持定时器"
- 窗口缩放选项(Window Scaling)用于高速网络
4.4 UDP协议:简单粗暴但快
UDP和TCP完全是两个路子。它不建立连接,不保证可靠,不重传丢包。那它有什么用?你想想看,视频通话、在线游戏、DNS查询——这些场景丢一两个包无所谓,但延迟高了就完蛋。
UDP的报文头只有8个字节:源端口、目的端口、长度、校验和。没了。我做过一个实时音视频项目,最开始用TCP传音频,延迟飙到500ms,用户骂娘。换成UDP后降到50ms,虽然偶尔有杂音,但体验好太多了。
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠(重传、确认) | 不可靠(尽最大努力) |
| 顺序 | 保证顺序 | 不保证顺序 |
| 开销 | 高(头部20字节+) | 低(头部8字节) |
| 速度 | 慢 | 快 |
| 典型应用 | HTTP、FTP、SMTP | DNS、DHCP、视频流 |
4.5 端口号:进程的"门牌号"
端口号用来区分同一台机器上的不同进程。IP地址找到机器,端口号找到机器上的程序。端口范围是0~65535,其中:
- 0~1023:知名端口,需要root权限才能用(HTTP=80, HTTPS=443, SSH=22)
- 1024~49151:注册端口,一般应用程序用
- 49152~65535:动态/私有端口,客户端临时使用
netstat -tlnp一看,端口被占了。记住:端口冲突是最常见的部署问题之一。
4.6 Socket编程基础
Socket是操作系统提供给应用层的编程接口。说白了,就是让你在代码里操作TCP或UDP的工具。不同语言封装不同,但核心流程一样。
TCP Socket编程流程:
// 服务端
socket() → bind() → listen() → accept() → recv()/send() → close()
// 客户端
socket() → connect() → send()/recv() → close()
UDP Socket编程流程:
// 服务端
socket() → bind() → recvfrom()/sendto() → close()
// 客户端
socket() → sendto()/recvfrom() → close()
看到区别了吗?UDP不需要listen和connect,直接发。我刚开始写UDP时,总习惯性地加connect,后来发现UDP的connect只是绑定对端地址,并不真的建立连接。
1. TCP服务端一定要处理accept返回的客户端fd,别搞混了
2. UDP的recvfrom能拿到发送端地址,回复时要用这个地址
3. 非阻塞Socket配合select/poll/epoll是高性能服务器的标配
嗯,传输层的内容就这些。TCP的可靠、UDP的轻量、端口的分工、Socket的编程模型——这些是网络编程的基石。你把这些吃透了,后面应用层协议学起来会轻松很多。