3. UDP数据报边界验证实验

UDP数据报边界验证实验 学习卡片

实验目标与核心问题

本实验要验证一个UDP的关键特性:UDP是面向数据报的协议,保留消息边界。客户端连续调用两次 sendto() 发送两条独立消息,服务端必须通过两次 recvfrom() 分别收到,而不会像TCP那样把两条消息合并成一条字节流。

实验设计要点:

  • 客户端发送两条内容不同、长度不同的数据报,例如 "ALPHA"(5字节)和 "BRAVO-12345"(11字节)。
  • 服务端循环接收,每次 recvfrom() 打印收到的字节数和内容,观察是否与发送端一一对应。
  • 关键判据:服务端第一次收到5字节,第二次收到11字节,而不是一次收到16字节。

实验拓扑与数据报边界关系

客户端 UDP Socket sendto() #1 "ALPHA" 5字节 sendto() #2 "BRAVO-12345" 11字节 两次独立系统调用 产生两个独立数据报 网络传输 UDP 数据报 数据报 A:5字节 数据报 B:11字节 边界保持,不合并 服务端 UDP Socket recvfrom() #1 收到 5字节 "ALPHA" recvfrom() #2 收到 11字节 "BRAVO-12345" 两次接收,边界清晰 不会合并为16字节 TCP 对比 可能合并为16字节字节流

客户端实现(Python)

客户端向 127.0.0.1:9003 连续发送两条独立数据报,中间不等待服务端响应,确保两条消息在时间上紧邻,更能体现边界保持特性。

import socket

SERVER_ADDR = ("127.0.0.1", 9003)

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

msg1 = b"ALPHA"
msg2 = b"BRAVO-12345"

sock.sendto(msg1, SERVER_ADDR)
print(f"[客户端] 已发送 #1: {msg1!r} ({len(msg1)}字节)")

sock.sendto(msg2, SERVER_ADDR)
print(f"[客户端] 已发送 #2: {msg2!r} ({len(msg2)}字节)")

sock.close()

服务端实现(Python)

服务端绑定同一端口,循环接收两次,每次打印收到的字节数和内容。注意:recvfrom() 每次只返回一个完整数据报,不会把两条消息拼在一起。

import socket

BIND_ADDR = ("0.0.0.0", 9003)

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(BIND_ADDR)
print(f"[服务端] 已绑定 {BIND_ADDR},等待数据报...")

for i in range(1, 3):
    data, addr = sock.recvfrom(2048)
    print(f"[服务端] 第{i}次接收: 来自 {addr}, "
          f"长度={len(data)}字节, 内容={data!r}")

sock.close()

运行与结果判读

先启动服务端,再运行客户端。预期输出如下:

[服务端] 已绑定 ('0.0.0.0', 9003),等待数据报...
[服务端] 第1次接收: 来自 ('127.0.0.1', 54321), 长度=5字节, 内容=b'ALPHA'
[服务端] 第2次接收: 来自 ('127.0.0.1', 54321), 长度=11字节, 内容=b'BRAVO-12345'
边界验证判据:
  • 第1次 recvfrom() 返回长度 5,内容 b'ALPHA'。
  • 第2次 recvfrom() 返回长度 11,内容 b'BRAVO-12345'。
  • 如果出现一次接收返回16字节、内容为 b'ALPHABRAVO-12345',则说明发生了字节流合并(UDP正常情况下不会出现)。
常见异常与排查:
  • 只收到一条16字节消息:检查是否误用了 SOCK_STREAM(TCP)而非 SOCK_DGRAM(UDP)。
  • 服务端阻塞在第一次 recvfrom:确认客户端已正确发送,且地址端口一致;可用 ss -ulnp | grep 9003 检查绑定状态。
  • 收到空数据报:UDP允许0字节数据报,若客户端发送空内容,服务端会收到长度0,这同样证明边界保留(空消息也是一个独立数据报)。

扩展验证:发送顺序与边界独立性

为进一步确认边界与顺序无关,可修改客户端发送顺序为 msg2 先发、msg1 后发,服务端将先收到11字节再收到5字节。这说明UDP保留的是每个数据报的边界,而不是固定顺序或固定长度。

发送顺序服务端第1次接收服务端第2次接收结论
ALPHA → BRAVO-123455字节 ALPHA11字节 BRAVO-12345边界保持
BRAVO-12345 → ALPHA11字节 BRAVO-123455字节 ALPHA边界保持,顺序随发送