本文目录导读:

- 案例一:最基础的“一发一收”通信(Python)
- 案例二:UDP 实现多客户端群聊(线程版)
- 案例三:UDP 实现简单的“文件传输”(带分片与序号)
- 案例四:真实世界中的UDP应用——DNS查询
- 案例五:UDP 的“不可靠性”演示(重传与丢包)
UDP(用户数据报协议)是网络通信中一种无连接、不可靠的传输层协议,与TCP的可靠流传输不同,UDP只负责发送数据包,不保证送达、不保证顺序,但开销低、延迟小,非常适合实时性要求高的场景(如视频通话、游戏、DNS查询)。
以下是几个由浅入深的UDP实战案例,包含核心代码和场景分析。
最基础的“一发一收”通信(Python)
场景:客户端发送“你好”,服务器接收并打印。
服务器端(udp_server.py)
import socket
# 1. 创建 UDP Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 绑定本地端口(IP地址留空表示绑定所有网卡)
server_address = ('0.0.0.0', 8888)
server_socket.bind(server_address)
print('服务器已启动,等待数据...')
# 3. 循环接收数据
while True:
data, client_addr = server_socket.recvfrom(1024) # 一次最多接收1024字节
print(f'收到来自 {client_addr} 的消息:{data.decode()}')
# 可选:回复客户端
reply = f'已收到你的消息'.encode()
server_socket.sendto(reply, client_addr)
客户端(udp_client.py)
import socket
# 1. 创建 UDP Socket(不需要连接)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 2. 发送数据给服务器(不需要 connect)
server_addr = ('127.0.0.1', 8888)
message = '你好,服务器!'.encode()
client_socket.sendto(message, server_addr)
# 3. 等待服务器回复(非必须,可设置超时防止卡死)
client_socket.settimeout(3) # 3秒收不到回复则报错
try:
reply, server_addr = client_socket.recvfrom(1024)
print(f'服务器回复:{reply.decode()}')
except socket.timeout:
print('等待超时,服务器未回复。')
UDP 实现多客户端群聊(线程版)
场景:多个客户端连接到同一个服务器,任意客户端发送的消息会广播给所有人。 关键点:UDP服务器使用字典记录所有客户端地址,收到消息后遍历转发。
import socket
import threading
# 全局变量:存储所有连接的客户端地址(IP, Port)
clients = set()
lock = threading.Lock()
def broadcast(message, sender_addr):
"""广播消息给所有其他客户端"""
with lock:
for client in clients:
if client != sender_addr: # 不发给发送者自己
server_socket.sendto(message, client)
# 创建服务器 Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('0.0.0.0', 9999))
print('群聊服务器启动...')
# 处理接收消息的函数(放在线程中运行)
def recv_handler():
while True:
data, addr = server_socket.recvfrom(1024)
# 将新地址添加到客户端列表
with lock:
if addr not in clients:
clients.add(addr)
print(f'新用户加入:{addr}')
# 转发消息
broadcast(data, addr)
# 启动接收线程(主线程等待)
thread = threading.Thread(target=recv_handler)
thread.daemon = True
thread.start()
# 主线程保持运行(例如按Ctrl+C退出)
try:
while True:
pass
except KeyboardInterrupt:
print('服务器关闭')
客户端只需创建socket后,通过sendto发送,并循环recvfrom接收广播即可。
UDP 实现简单的“文件传输”(带分片与序号)
场景:发送大文件时,由于UDP单包上限(MTU通常1500字节),需要将文件分片发送,并加上序号方便重组。 注意:此案例不实现重传(那是RUDP/QUIC做的事),仅演示分片逻辑。
import socket
import os
def send_file(file_path, server_addr):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
file_size = os.path.getsize(file_path)
# 1. 先发送文件信息(文件名长度+文件名+文件大小)
file_name = os.path.basename(file_path).encode()
meta = f'{len(file_name)}:'.encode() + file_name + f':{file_size}'.encode()
s.sendto(b'META|' + meta, server_addr)
# 2. 分片发送内容(每片 1400 字节,留出头部空间)
seq = 0
with open(file_path, 'rb') as f:
while True:
chunk = f.read(1400)
if not chunk:
break
# 数据包格式:[序号(4字节)][数据]
packet = seq.to_bytes(4, 'big') + chunk
s.sendto(packet, server_addr)
seq += 1
# 3. 发送结束标志
s.sendto(b'END|' + seq.to_bytes(4,'big'), server_addr)
print(f'文件发送完成,共 {seq} 个分片')
# 接收端(伪代码)
def recv_file(server_socket):
# 1. 接收META,解析文件名和大小
# 2. 循环接收分片,根据序号存入字典
# 3. 收到END后,按顺序写文件
pass
真实世界中的UDP应用——DNS查询
场景:通过UDP直接向DNS服务器(如8.8.8.8)发送查询报文,获取域名IP。 特点:UDP自带“请求-响应”模式,非常适合这种短小查询。
import socket
import struct
# 1. 构造DNS查询报文(精简版)
def build_dns_query(domain):
header = struct.pack('>HHHHHH', 0x1234, 0x0100, 1, 0, 0, 0) # 事务ID, 标志, 问题数...
qname = b''.join(bytes([len(part)]) + part.encode() for part in domain.split('.')) + b'\x00'
qtype = struct.pack('>HH', 1, 1) # A记录, IN类
return header + qname + qtype
# 2. 发送并接收
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(5)
dns_server = ('8.8.8.8', 53)
query = build_dns_query('www.example.com')
s.sendto(query, dns_server)
try:
response, _ = s.recvfrom(512)
# 解析响应中的IP地址(简化处理,只找第一个A记录)
current_index = 12 # 跳过头部
# ...复杂解析省略...
print('DNS响应已接收')
except socket.timeout:
print('DNS查询超时')
UDP 的“不可靠性”演示(重传与丢包)
目的:展示UDP不保证送达,需要上层自己处理丢包。
import socket
import random
import time
# 模拟一个丢包率为50%的服务器
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('0.0.0.0', 7777))
print('丢包服务器启动(50%概率丢弃消息)')
while True:
data, addr = server_socket.recvfrom(1024)
if random.random() < 0.5:
print(f'丢弃来自 {addr} 的数据包')
continue # 直接不回复
reply = b'OK'
server_socket.sendto(reply, addr)
# 客户端需要设置超时并重试
client_socket.settimeout(1)
for attempt in range(5):
client_socket.sendto(b'ping', server_addr)
try:
reply = client_socket.recvfrom(1024)
print('成功收到回复')
break
except socket.timeout:
print(f'第 {attempt+1} 次尝试超时,重试...')
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 需三次握手 | 无连接,直接发送 |
| 可靠性 | 可靠(重传、排序、流控) | 不可靠(可能丢失、乱序) |
| 速度与开销 | 慢,头部20字节,系统开销大 | 快,头部8字节,开销极小 |
| 应用场景 | Web、邮件、文件传输 | 实时语音/视频、游戏、监控 |
| 数据边界 | 字节流(无边界) | 数据报(有边界,保留消息边界) |
编程提示:
- 使用
sendto()发送时必须指定目标地址。 - 使用
recvfrom()接收时会返回数据包大小限制内的内容,一次接收一个完整UDP报文,不会像TCP那样拆包粘包。 - 如果需要可靠传输,你必须自己在应用层实现:确认机制(ACK)、超时重传、序列号、流量控制(这就是RUDP/QUIC的原理)。
案例涵盖了UDP的基础用法、广播实现、分片传输和实际网络协议应用,初学者建议从案例一开始动手,逐步加深理解。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播