3. UDP消息发送与接收

UDP消息发送与接收 学习卡片

任务目标

实现一个最小可运行的UDP通信程序:服务器端绑定固定端口并循环接收客户端发来的数据报,客户端向该端口发送一条文本消息并接收服务器回显的应答。通过该闭环理解UDP的无连接特性——通信前不需要建立连接,数据报直接发往目标地址端口。

环境与依赖

仅使用Python 3标准库socket,无需安装第三方包。代码在Windows/Linux/macOS均可运行。

完整实现

# udp_demo.py
import socket

SERVER_HOST = "127.0.0.1"
SERVER_PORT = 8888
BUFFER_SIZE = 1024

def run_server():
    """UDP服务器:绑定端口,循环接收消息并回显"""
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_sock.bind((SERVER_HOST, SERVER_PORT))
    print(f"[服务器] 已启动,监听 {SERVER_HOST}:{SERVER_PORT}")

    while True:
        data, client_addr = server_sock.recvfrom(BUFFER_SIZE)
        message = data.decode("utf-8")
        print(f"[服务器] 收到来自 {client_addr} 的消息: {message}")

        # 回显应答
        reply = f"服务器已收到: {message}"
        server_sock.sendto(reply.encode("utf-8"), client_addr)

def run_client():
    """UDP客户端:发送一条消息,等待服务器应答后退出"""
    client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    message = "Hello UDP Server"
    client_sock.sendto(message.encode("utf-8"), (SERVER_HOST, SERVER_PORT))
    print(f"[客户端] 已发送: {message}")

    # 等待应答(设置超时避免无限阻塞)
    client_sock.settimeout(3)
    try:
        data, _ = client_sock.recvfrom(BUFFER_SIZE)
        print(f"[客户端] 收到应答: {data.decode('utf-8')}")
    except socket.timeout:
        print("[客户端] 等待应答超时(服务器可能未启动)")
    finally:
        client_sock.close()

if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("用法: python udp_demo.py [server|client]")
        sys.exit(1)

    if sys.argv[1] == "server":
        run_server()
    elif sys.argv[1] == "client":
        run_client()
    else:
        print("参数错误,请输入 server 或 client")
        sys.exit(1)

运行与验证

打开两个终端窗口,均进入代码所在目录:

  1. 启动服务器(终端1):python udp_demo.py server,看到监听日志即表示服务器就绪。
  2. 启动客户端(终端2):python udp_demo.py client。

通过条件:

  • 客户端打印“已发送”和“收到应答: 服务器已收到: Hello UDP Server”;
  • 服务器端打印收到来自127.0.0.1随机端口(如('127.0.0.1', 54321))的消息,内容为Hello UDP Server。

若先启动客户端再启动服务器,客户端会在3秒后打印超时提示——这正是UDP无连接的体现:发送方不关心接收方是否在线,数据报发出即完成发送动作。

关键说明

  • UDP使用SOCK_DGRAM套接字类型,sendto/recvfrom每次调用都需携带目标地址或获取来源地址,因为无连接意味着每个数据报独立寻址。
  • 服务器端bind固定端口后,可连续接收任意客户端发来的数据报,无需accept操作。
  • 客户端未绑定端口,由操作系统自动分配临时端口,因此服务器回显时使用recvfrom返回的client_addr作为目标地址。
UDP 客户端 socket(AF_INET, SOCK_DGRAM) ① sendto("Hello UDP Server") ② recvfrom() 等待应答 ③ 打印应答内容 UDP 服务器 bind(("127.0.0.1", 8888)) ① recvfrom() 阻塞等待 ② 获取消息与来源地址 ③ sendto(应答, client_addr) 数据报 "Hello UDP Server" 应答数据报 "服务器已收到: ..." 无连接特性:双方无需握手,数据报独立发送,服务器不在线时发送方无感知