🌈个人主页:一条泥憨鱼(欢迎各位大佬莅临)

🎬精选专栏传送门:
❄️《数据结构》 ❄️《AI与Agent那些事》
❄️《从0开始学计算机网络》 ❄️《后端开发》

前言:
你上个月是不是也遇到过一次:给朋友寄了个文件,快递员放楼下快递柜就走了,连个电话都没打。你朋友第二天才看到取件码,气得不行。
但换个场景——你寄的是合同原件,快递员非要你当面签收,还让你报身份证号。你嫌麻烦,可又觉得踏实。
网络世界里,数据就是这么送的。而负责“送数据”的两种主要方式,就是 TCP 和 UDP。一个像那个不管不顾的快递员,一个像那个非要签收的快递员。
今天就把这俩讲明白。
先搞清楚:网络里“发消息”到底是怎么回事
在聊 TCP 和 UDP 之前,得先知道网络里数据是怎么从一台电脑到另一台电脑的。这里没有任何高深的东西。
假设你要给朋友发一个 10MB 的照片文件。网络传输有个特点:它不喜欢一次性搬大块头。就像搬家不会把整个衣柜原封不动抬走,而是拆成板子、装进箱子、贴上标签再运。网络也一样,会把你的大文件切成一小块一小块,每块单独发送。
这一小块一小块的数据,专业术语叫数据包(Packet)。

每个数据包上面都写着两样关键信息:
1. IP 地址——这就相当于收件人的门牌号。比如 `192.168.1.100`,这串数字在全世界范围内唯一标识一台设备。你朋友的电脑有一个,他手机也有一个,他家路由器也有一个。
2. 端口号——光有门牌号还不够。你朋友的电脑上可能同时开着微信、浏览器、游戏,数据包到了他家门口,得知道该交给哪个程序。端口号就像是门牌号里的“房间号”。比如 80 端口通常是网页服务,443 是加密网页服务,你自己写的程序可以随便用一个没被占用的端口。
所以,数据包的完整地址应该是:IP 地址 + 端口号,合起来叫套接字(Socket)。不过这个术语今天用不上,你只要记住:发数据 = 把数据切成块,每块写上对方的 IP 和端口,然后扔到网络里。
问题来了:扔出去之后,怎么保证对方能收到?这就是 TCP 和 UDP 分道扬镳的地方。
UDP——放下就跑的快递员
UDP(User Datagram Protocol,用户数据报协议)这个快递员,风格就是:包裹放下,转身就走,爱签不签。
你寄一张明信片,扔进邮筒,然后呢?没有人会通知你“明信片已送达”,也不会有人告诉你“对方收到没”。丢了就丢了,你甚至不知道它丢了。
UDP 就是这个逻辑。发数据包的时候,它不会先跟对方打个招呼,也不管对方在不在线,直接把数据包扔进网络里。对方收到了就收到了,没收到也不会重发。
专业术语叫无连接——发送之前不需要建立连接。以及不可靠——不保证送达,不保证顺序,不保证不丢包。
看代码可能更直观。这是 Python 里用 UDP 发消息的最简示例:
import socket
# 创建 UDP socket,SOCK_DGRAM 就是 UDP 的意思
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 目标地址:IP + 端口
target = ("192.168.1.100", 8888)
# 直接发,不需要先连接,不需要确认对方在不在
sock.sendto(b"Hello, UDP!", target)
# 完事。对,就这么简单,没有握手,没有确认。
```
接收端就更简单了:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", 8888)) # 监听所有网卡的 8888 端口
while True:
data, addr = sock.recvfrom(1024) # 收到数据包和来源地址
print(f"收到来自 {addr} 的消息: {data.decode()}")
```
你发现没有,整个过程没有“你一句我一句”的确认环节。发完就走,干净利落。
那这种“不靠谱”的协议有什么用?用处大了。
视频通话。你和朋友视频聊天,画面一帧一帧传过来。如果某一帧丢了,最合理的策略是什么?丢掉就丢掉了,下一帧马上就来。如果为了补一帧画面而停下来等待重传,那整个视频会卡成 PPT。人眼对短暂的画面缺失不敏感,但对卡顿极其敏感。
直播同理。主播说了一句话,中间丢了一秒的声音,观众根本察觉不到。但如果因为丢包而停下来缓冲,那体验就是灾难。
在线游戏也是。你操控角色往前走,这个操作指令必须尽快发到服务器。如果走 UDP 丢了几个包,最多角色卡顿一下;如果走 TCP 因为等待重传而延迟半秒,你可能已经被对面击杀了。
一句话总结 UDP 的场景:可以容忍少量丢失,但绝对不能容忍延迟。

TCP——必须签收的快递
TCP(Transmission Control Protocol,传输控制协议)是另一个极端。它像寄贵重物品的快递:先打电话确认你在家,然后上门,你当面签收,签完字还要拍个照存档。
TCP 的第一个特点是面向连接——在正式传数据之前,双方先“打个招呼”,确认彼此都在线,然后才开工。
这个打招呼的过程,专业术语叫三次握手(Three-way Handshake)。用大白话说就是:
1. 你(客户端)对服务器说:“喂,你在吗?我想传数据。”(这叫 SYN)
2. 服务器回你:“在的,在的,我准备好了,可以开始。”(这叫 SYN-ACK)
3. 你再说:“好,收到,那我开始了。”(这叫 ACK)
三句话说完,连接建立,开始传数据。 
那你可能会问:为什么非得三次?两次行不行?比如你发“在吗”,对方回“在”,不就算确认了吗?
不行。因为网络可能丢包。如果对方回的“在”丢了,你以为对方不在线,就不会开始发数据。三次握手的本质是:双方都要确认“我能收到你,你也能收到我”。第三次的 ACK 就是让服务器知道“客户端确实收到了我的回复”。这样就确保了两边都确认了对方在线。
连接建立之后,TCP 还要保证可靠传输。可靠体现在三件事:
第一,确认机制。每收到一个数据包,接收方就回一个“收到了”的确认包(ACK)。就像快递员等你签收,签了字才算数。
第二,重传机制。如果发送方发出去一个包,等了一会儿没收到确认,就默认丢了,重新发一遍。就像快递员打电话给你没打通,过一会儿再打。
第三,顺序保证。数据包走的路径可能不一样,到达的顺序可能是乱的。TCP 会给每个包编上序号,接收方按序号重新排好,再交给上层应用。就像快递单上有编号,你收到的包裹即使乱序,也能按编号整理好。
Python 里用 TCP 发送数据大概是这样的:
import socket
# 创建 TCP socket,SOCK_STREAM 就是 TCP
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# TCP 必须首先建立连接——这就是三次握手
sock.connect(("192.168.1.100", 8888))
# 连接建立后,可以直接发送数据
sock.sendall(b"Hello, TCP!")
# 等待对方的确认回复
response = sock.recv(1024)
print(f"对方确认收到: {response.decode()}")
sock.close()
```
接收端:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("0.0.0.0", 8888))
sock.listen(5) # 开始监听,最多允许 5 个连接排队
while True:
conn, addr = sock.accept() # 接受连接——三次握手完成
data = conn.recv(1024)
print(f"收到来自 {addr} 的消息: {data.decode()}")
conn.sendall(b"ACK: 我收到了!")
conn.close()
```
同样是一段代码,对比 UDP 那个版本,你发现关键差异了吗?TCP 要先 `connect()`,这就是三次握手。发完数据还要等对方回应。UDP 没有这一切。
TCP 适合什么场景?一点都不能丢的场景。
网页浏览。你打开一个网页,HTML、CSS、JavaScript、图片,任何一个文件缺失都会导致页面显示错乱。所以浏览器和服务器之间用 TCP。
文件下载。下载一个安装包,哪怕只缺一个字节,这个文件就是坏的,装不上。必须用 TCP。
邮件。邮件内容一个字符都不能丢。发出去的邮件如果少了一段,那麻烦大了。TCP。
游戏里的登录系统、交易系统,也基本都是 TCP。因为这些操作必须要可靠,你不想登录的时候账号信息丢了导致上不了号。
那到底怎么选?一张表看懂
到了这里,你应该已经能判断了。我给你整理一张对比表:
| 维度 | TCP | UDP |
| 连接 | 需要先建立连接(三次握手) | 无连接,直接发 |
| 可靠性 | 可靠:确认、重传、保序 | 不可靠:可能丢包、乱序 |
| 速度 | 慢(有确认和重传的开销) | 快(没有额外开销) |
| 数据量 | 适合大文件传输 | 适合实时小数据 |
| 典型场景 | 网页、文件下载、邮件、登录 | 视频通话、直播、游戏 |
看视频用哪个?你在视频网站上点开一个视频,这个场景其实两个都有。视频文件本身是通过 TCP 下载的(一次下载完,之后本地播放)。但如果你在看直播,那必须是 UDP。区别在于:录播视频可以慢慢下载,直播必须实时。
发微信消息用哪个?微信文字消息是 TCP。你发“在吗”,对方必须收到,不能丢。但微信语音通话是 UDP——实时通话,丢几帧语音听不出来,但卡顿完全不能忍。
打王者荣耀用哪个?游戏本身的操作指令走 UDP(要求低延迟),但登录验证、购买皮肤这些操作走 TCP(要求可靠)。一个游戏里两种协议混着用,很正常。
下载大文件用哪个?必须是 TCP。你下载一个 2GB 的压缩包,中间丢了一个数据包,整个文件就损坏了。TCP 的重传机制在这里就是生命线。
核心判断标准:如果数据丢了会导致严重后果,选 TCP;如果延迟比丢包更致命,选 UDP。
没有谁更好,只有谁更合适。TCP 是“稳”,UDP 是“快”。你在两者之间选,本质上是在选“你能接受什么代价”。
小结
TCP 和 UDP 是网络传输的两种基本方式,你可以理解为两个性格完全不同的快递员:
– UDP:放下就跑,不确认、不重传、不保序。适合视频通话、直播、游戏——这些场景能容忍少量丢包,但绝对不能容忍延迟。
– TCP:先打招呼(三次握手),每步都确认,丢了就重传。适合网页、文件下载、邮件——这些场景一个字节都不能少,慢一点没关系。
下一步,如果你装了 Python,可以把上面两个代码示例跑一下。先跑 UDP 的,再跑 TCP 的,感受一下“无连接”和“面向连接”的差异。你会发现 TCP 那个 `connect()` 和等回复的过程,就是它可靠性的来源。



