PYTHON网络编程——UDP篇

一、UDP简介

什么是UDP?

UDP(用户数据报协议)是一种无连接的传输层协议,具有以下特点:

  • 无需建立连接
  • 不可靠传输(可能丢包)
  • 传输速度快
  • 适合实时性要求高的场景

基本流程对比

1
2
TCP编程流程:创建socket → 绑定 → 监听 → 接受连接 → 收发数据 → 关闭
UDP编程流程:创建socket → 绑定(服务端)→ 直接收发数据 → 关闭

二、服务端代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import socket

# 创建了一个UDP的socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# socket.AF_INET: 使用IPv4地址族
# socket.SOCK_DGRAM: 使用UDP协议(数据报套接字)

# 前一个是我的ipv4地址,如果用127.0.0.1则是在本地
# 如果是空字符,服务端绑定到所有的ip地址
server_socket.bind(('192.168.100.199', 6666))
# bind()方法将套接字绑定到指定地址和端口
# '192.168.100.199': 绑定到特定局域网IP
# 其他选择:
# '127.0.0.1': 本地回环,只能本机访问
# '0.0.0.0': 绑定所有网络接口
# '': 同'0.0.0.0'
# 6666: 端口号,范围0-65535(0-1023为系统保留)

while True:
# msg是收到的数据,addr是源地址和端口号
msg, addr = server_socket.recvfrom(1024)
# recvfrom()是阻塞方法,会一直等待直到收到数据
# 1024: 缓冲区大小,单位字节
# msg: 接收到的字节数据
# addr: 元组 (客户端IP, 客户端端口)

if msg.decode('utf8') == 'quit':
break # 如果客户端发送'quit',则退出循环

print(f'来自IP:{addr[0]},端口号:{addr[1]}的消息:{msg}')
# 显示消息来源和内容

send_msg = input('服务端>>')
# 等待用户输入回复内容

# 不能发送字符串,应该是字节数据
server_socket.sendto(send_msg.encode('utf8'), addr)
# sendto()发送数据到指定地址
# encode('utf8'): 将字符串转为字节数据
# addr: 目标地址(这里发回给原客户端)

# close
server_socket.close()
# 关闭套接字,释放资源

三、客户端代码详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import socket

# 创建了一个UDP的socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 客户端socket不用bind
# 客户端通常由系统自动分配端口号
# 如果需要绑定特定端口,也可以使用bind()

while True:
# send msg
send_msg = input('客户端>>')
if send_msg == 'quit':
break # 输入'quit'退出

client_socket.sendto(send_msg.encode('utf8'), ('192.168.100.199', 6666))
# 发送数据到服务器
# ('192.168.100.199', 6666): 服务器地址和端口
# 必须与服务器绑定的地址一致

# receive msg
msg, addr = client_socket.recvfrom(1024)
# 等待服务器回复
# 注意:这里会阻塞,直到收到服务器响应

print(f'来自服务端IP:{addr[0]},端口号:{addr[1]}的消息:{msg}')
# 显示服务器回复

client_socket.close()
# 关闭客户端套接字

四、关键知识点

1. UDP套接字创建

1
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  • AF_INET: IPv4地址族
  • SOCK_DGRAM: UDP数据报类型

2. 地址绑定

1
2
# 服务端必须绑定,客户端可选
server_socket.bind((IP地址, 端口号))

3. 数据收发

1
2
3
4
5
# 接收数据(返回数据和来源地址)
data, addr = socket.recvfrom(缓冲区大小)

# 发送数据到指定地址
socket.sendto(字节数据, (目标IP, 目标端口))

4. 编码转换

1
2
3
4
5
# 发送:字符串 → 字节
send_msg.encode('utf8')

# 接收:字节 → 字符串
msg.decode('utf8')

五、注意事项

  1. UDP是无连接的

    • 每次发送都要指定目标地址
    • 不保证数据顺序和可靠性
  2. 地址和端口

    • 服务端需要固定端口
    • 客户端端口通常由系统分配
  3. 数据大小

    • UDP数据包不宜过大(通常<1500字节)
    • 避免IP分片,提高传输效率
  4. 阻塞问题

    • recvfrom()是阻塞调用
    • 程序会等待直到收到数据
  5. 多客户端处理

    • UDP服务端可以同时处理多个客户端
    • 通过addr区分不同客户端

六、常见问题

Q1: 为什么客户端不需要bind?

A: 客户端第一次调用sendto()时,系统会自动分配一个可用端口。

Q2: 如果服务器没启动,客户端会怎样?

A: 客户端发送的数据会丢失,recvfrom()会一直等待(阻塞)。

Q3: 如何测试本机通信?

A: 服务端绑定127.0.0.1,客户端连接127.0.0.1

Q4: 如何让其他电脑连接?

A:

  1. 服务端绑定0.0.0.0或局域网IP
  2. 关闭防火墙或开放对应端口
  3. 客户端使用服务器实际IP地址

七、完整的UDP还需要

  1. 异常处理:添加try-except处理网络错误
  2. 超时设置:使用settimeout()避免永久阻塞
  3. 多线程:同时处理多个客户端请求
  4. 数据验证:添加简单的协议头验证数据完整性

八、总结

​ UDP编程的核心模式:

  • 服务端:创建→绑定→循环收发→关闭
  • 客户端:创建→循环收发→关闭

这种简单的请求-响应模式是UDP编程的基础,理解了这种模式后,可以在此基础上构建更复杂的UDP应用。


注:实际使用时,请确保服务端和客户端的IP地址和端口号配置正确,防火墙已开放相应端口。