第5章 - TCP 与 UDP 协议
嗨,朋友!
前面几章我们学习了网络层的 IP 地址,现在我们来学习传输层的两大核心协议:TCP 和 UDP。
很多人觉得这两个协议很抽象,但其实它们就像生活中的两种通信方式。我用一个简单的类比,保证让你一听就懂!
🤔 TCP vs UDP:打电话 vs 发短信
TCP(传输控制协议)📞
类比:打电话
你: "喂,听得到吗?"
朋友: "听得到!"
你: "我要说三件事"
朋友: "好的,你说"
你: "第一件事是..."
朋友: "收到第一件事"
你: "第二件事是..."
朋友: "收到第二件事"
你: "好了,我说完了"
朋友: "好的,我也说完了"
特点:
- ✅ 建立连接(先打通电话)
- ✅ 可靠传输(确认对方收到)
- ✅ 按顺序传输(第一句话先到,第二句话后到)
- ❌ 速度较慢(需要确认)
- ❌ 开销较大(需要维持连接)
UDP(用户数据报协议)📨
类比:发短信
你: "今天晚上吃火锅!"(发送)
你: "记得带钱!"(发送)
你: "7点楼下见!"(发送)
→ 不管对方收没收到,你就发出去了
→ 可能顺序乱了(第三条先到,第一条后到)
→ 可能丢失(某条短信没收到)
特点:
- ✅ 无需建立连接(直接发)
- ✅ 速度快(不需要确认)
- ✅ 开销小(不维持连接)
- ❌ 不可靠(可能丢包)
- ❌ 无序传输(顺序可能乱)
📊 TCP vs UDP 对比表
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输 | 不可靠传输 |
| 传输顺序 | 保证顺序 | 不保证顺序 |
| 传输速度 | 较慢 | 较快 |
| 资源开销 | 较大 | 较小 |
| 应用场景 | 文件传输、网页浏览、邮件 | 视频直播、语音通话、游戏 |
| 常见协议 | HTTP、HTTPS、FTP、SMTP | DNS、DHCP、视频流 |
🔌 端口号
什么是端口号?
类比:IP 地址是"小区地址",端口号是"房间号"
你家地址: 北京市朝阳区XX小区XX号楼
↑
IP 地址 (例如: 192.168.1.100)
你家房间号: 1001室、1002室、1003室
↑
端口号 (例如: 80, 443, 3000)
一台电脑上可以运行多个网络程序:
你的电脑 (IP: 192.168.1.100)
├── 浏览器 → 使用随机端口 (如 54321)
├── 微信 → 使用特定端口
├── 本地服务器 → 使用端口 3000
└── 数据库 → 使用端口 3306
端口号范围
| 端口范围 | 名称 | 说明 | 示例 |
|---|---|---|---|
| 0-1023 | 知名端口 | 系统保留,常用服务 | 80(HTTP)、443(HTTPS)、22(SSH) |
| 1024-49151 | 注册端口 | 注册的应用程序 | 3306(MySQL)、5432(PostgreSQL) |
| 49152-65535 | 动态端口 | 临时使用 | 客户端随机分配 |
常用端口号
| 端口 | 协议 | 服务 | 说明 |
|---|---|---|---|
| 20/21 | TCP | FTP | 文件传输 |
| 22 | TCP | SSH | 安全远程登录 |
| 23 | TCP | Telnet | 远程登录 |
| 25 | TCP | SMTP | 发送邮件 |
| 53 | UDP | DNS | 域名解析 |
| 80 | TCP | HTTP | 网页浏览 |
| 443 | TCP | HTTPS | 加密网页浏览 |
| 3306 | TCP | MySQL | 数据库 |
| 3000 | TCP | Node.js | 常用开发端口 |
| 8080 | TCP | HTTP | 备用HTTP端口 |
🔍 TCP 详解
TCP 的核心特性
1. 面向连接(Connection-Oriented)🤝
三次握手建立连接:
客户端 服务器
│ │
│──① SYN(我想连接你)──→│
│ │
│←─② SYN+ACK(好的)─────│
│ │
│──③ ACK(收到,开始吧)→│
│ │
│ 开始传输数据 │
四次挥手断开连接:
客户端 服务器
│ │
│──① FIN(我要关闭了)──→│
│ │
│←─② ACK(我知道了)─────│
│ │
│←─③ FIN(我也关闭了)───│
│ │
│──④ ACK(再见)────────→│
│ │
连接关闭
2. 可靠传输(Reliable Transmission)✅
确认应答机制:
发送方: 发送数据包1
接收方: 收到数据包1,发送ACK1
发送方: 收到ACK1,发送数据包2
接收方: 收到数据包2,发送ACK2
...
超时重传:
发送方: 发送数据包1
接收方: (数据包丢失,没收到)
发送方: 等待超时 → 重新发送数据包1
接收方: 收到数据包1,发送ACK1
3. 流量控制(Flow Control)🚦
防止发送方发送太快,接收方处理不过来:
接收方: "我的缓冲区还有 1024 字节"
发送方: "好的,我只发 1024 字节"
接收方: "我处理了一些,现在还有 2048 字节"
发送方: "好的,我现在可以发 2048 字节"
4. 拥塞控制(Congestion Control)🚥
防止网络拥堵:
刚开始: 慢慢发(慢启动)
网络好: 加快发送速度
发现丢包: 减慢速度(拥塞避免)
TCP 报文结构
┌────────────────────────────────────┐
│ 源端口 (16位) │ 目标端口 (16位) │
├────────────────────────────────────┤
│ 序号 (32位) │
├────────────────────────────────────┤
│ 确认序号 (32位) │
├────────────────────────────────────┤
│ 标志位 │ 窗口大小 │
├────────────────────────────────────┤
│ 校验和 │ 紧急指针 │
├────────────────────────────────────┤
│ 数据 │
└────────────────────────────────────┘
📡 UDP 详解
UDP 的核心特性
1. 无连接(Connectionless)🚀
不需要握手,直接发送:
客户端 服务器
│ │
│──数据包1────────────────→│
│──数据包2────────────────→│
│──数据包3────────────────→│
│ │
2. 不可靠传输(Unreliable)⚠️
发送方不管数据包是否到达:
发送方: 发送数据包1 → 可能到达 ✅
发送方: 发送数据包2 → 可能丢失 ❌
发送方: 发送数据包3 → 可能到达 ✅
→ 发送方不知道哪些到达了,哪些丢失了
3. 轻量级(Lightweight)⚡
UDP 头部只有 8 字节,TCP 头部至少 20 字节
UDP 头部: 8 字节
TCP 头部: 20-60 字节
→ UDP 开销更小,速度更快
UDP 报文结构
┌────────────────────────────────────┐
│ 源端口 (16位) │ 目标端口 (16位) │
├────────────────────────────────────┤
│ 长度 (16位) │ 校验和 (16位) │
├────────────────────────────────────┤
│ 数据 │
└────────────────────────────────────┘
简洁得多!
🎯 应用场景选择
使用 TCP 的场景 ✅
网页浏览(HTTP/HTTPS)
- 需要完整加载网页内容
- 不能丢失任何数据
文件传输(FTP)
- 文件必须完整
- 不能有任何损坏
邮件发送(SMTP)
- 邮件内容必须准确
- 不能丢失信息
数据库连接(MySQL)
- 数据必须准确无误
- 不能有丢失或错乱
// Node.js 使用 TCP 示例
const net = require('net');
// 创建 TCP 服务器
const server = net.createServer((socket) => {
console.log('客户端已连接');
socket.on('data', (data) => {
console.log('收到数据:', data.toString());
socket.write('服务器已收到');
});
});
server.listen(3000, () => {
console.log('TCP 服务器运行在 3000 端口');
});
使用 UDP 的场景 ✅
视频直播/语音通话
- 丢失几个数据包影响不大
- 速度比完整性更重要
在线游戏
- 需要低延迟
- 偶尔丢包可以接受(下一帧会更新)
DNS 查询
- 数据量小
- 查询简单快速
IoT 设备
- 设备资源有限
- 数据简单
// Node.js 使用 UDP 示例
const dgram = require('dgram');
// 创建 UDP 服务器
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`收到来自 ${rinfo.address}:${rinfo.port} 的消息: ${msg}`);
});
server.bind(3000, () => {
console.log('UDP 服务器运行在 3000 端口');
});
💻 实际应用案例
前端开发
HTTP 请求(使用 TCP)
// Fetch API(底层使用 TCP)
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => {
console.log('数据完整接收:', data);
});
// 特点:
// - 使用 TCP 协议
// - 数据完整、可靠
// - 适合获取关键数据
WebSocket(使用 TCP)
// 实时聊天(底层使用 TCP)
const socket = new WebSocket('ws://localhost:3000');
socket.onopen = () => {
console.log('连接已建立');
socket.send('Hello Server');
};
socket.onmessage = (event) => {
console.log('收到消息:', event.data);
};
// 特点:
// - 使用 TCP 协议
// - 保持长连接
// - 消息可靠传输
WebRTC(可使用 UDP)
// 视频通话(可以使用 UDP)
const peerConnection = new RTCPeerConnection();
// WebRTC 可以选择 UDP 来降低延迟
// 适合实时音视频通话
后端开发
Express 服务器(TCP)
const express = require('express');
const app = express();
// HTTP 服务器使用 TCP
app.get('/users', (req, res) => {
res.json({ users: ['张三', '李四'] });
});
app.listen(3000, () => {
console.log('服务器运行在 3000 端口(TCP)');
});
UDP 服务器
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`UDP 消息: ${msg} 来自 ${rinfo.address}:${rinfo.port}`);
// 发送响应
server.send('收到', rinfo.port, rinfo.address);
});
server.bind(4000);
运维配置
# 查看 TCP 连接
netstat -an | grep tcp
# 查看指定端口的 TCP 连接
netstat -an | grep :3000
# 查看 UDP 连接
netstat -an | grep udp
# Linux 防火墙配置
# 开放 TCP 端口 80
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 开放 UDP 端口 53(DNS)
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
测试工程师
# 使用 telnet 测试 TCP 连接
telnet example.com 80
# 使用 nc(netcat)测试 TCP
nc -v example.com 80
# 使用 nc 测试 UDP
nc -u -v example.com 53
# 使用 curl 测试 HTTP(TCP)
curl -v http://example.com
🔬 实验:观察 TCP 和 UDP
实验1:查看 TCP 连接
# Windows
netstat -an | findstr ESTABLISHED
# Linux/Mac
netstat -an | grep ESTABLISHED
# 你会看到类似这样的输出:
# TCP 192.168.1.100:54321 93.184.216.34:443 ESTABLISHED
# ↑ ↑ ↑
# 你的IP和端口 服务器IP和端口 已建立连接
实验2:抓包分析
使用 Wireshark 抓包工具:
- 打开 Wireshark
- 选择网卡,开始抓包
- 访问网站(例如 www.baidu.com)
- 过滤 TCP 包:
tcp - 观察三次握手过程
你会看到:
1. SYN(客户端 → 服务器)
2. SYN+ACK(服务器 → 客户端)
3. ACK(客户端 → 服务器)
💡 长安的学习建议
- 记住核心区别 - TCP 可靠慢,UDP 快但不可靠
- 理解应用场景 - 根据需求选择协议
- 动手实验 - 用 netstat 查看自己电脑的连接
- 联系实际 - 想想你用的应用都用了什么协议
📝 小结
这一章我们学习了:
✅ TCP:面向连接、可靠传输、有序传输、适合文件和网页
✅ UDP:无连接、不可靠、快速、适合视频和游戏
✅ 端口号:区分同一台电脑上的不同程序
✅ TCP 三次握手:建立连接的过程
✅ 应用场景:根据需求选择合适的协议
✅ 实际应用:前端、后端、运维、测试都在用
🎯 下一步
现在你已经理解了传输层的 TCP 和 UDP,接下来我们要学习应用层最重要的协议:HTTP 和 HTTPS!
💪 练习题
- 什么时候应该使用 TCP?什么时候应该使用 UDP?
- 查看你的电脑上有哪些 TCP 连接(使用 netstat 命令)
- 为什么视频直播用 UDP 而不是 TCP?
答案提示
TCP vs UDP 使用场景:
- 使用 TCP:需要数据完整、可靠时
- 网页浏览、文件下载、邮件发送、数据库连接
- 使用 UDP:需要速度快、可以容忍少量丢包时
- 视频直播、语音通话、在线游戏、DNS 查询
- 使用 TCP:需要数据完整、可靠时
查看 TCP 连接:
# Windows netstat -an | findstr TCP # Linux/Mac netstat -an | grep tcp你会看到很多连接,包括浏览器、微信、各种应用的网络连接
为什么视频直播用 UDP?
- 视频直播对实时性要求高,延迟要低
- 丢失几帧画面影响不大,用户可能都注意不到
- 如果用 TCP,重传丢失的数据包会增加延迟
- UDP 直接发送,不等待确认,延迟更低
- 总结:速度比完整性更重要
