3. UDP时间服务器

UDP时间服务器 学习卡片

任务目标

实现一个基于UDP协议的时间服务器:客户端向服务器发送任意数据报(通常为空或单字节),服务器收到后立即将当前时间以文本形式作为响应数据报发回客户端。通过该案例理解UDP的无连接特性——通信双方无需建立连接,每个数据报独立发送和接收。

环境与依赖

  • Python 3.6+(标准库 socket,无需第三方依赖)
  • 操作系统:Windows / Linux / macOS 均可

完整实现

以下代码包含服务器与客户端两个独立脚本。服务器绑定本机所有接口的 12345 端口,循环接收数据报并回发当前时间;客户端向指定地址发送一个空数据报,然后阻塞等待服务器响应并打印结果。

# udp_time_server.py
import socket
from datetime import datetime

HOST = '0.0.0.0'   # 监听所有网络接口
PORT = 12345

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind((HOST, PORT))
print(f'UDP时间服务器已启动,监听端口 {PORT}')

while True:
    data, client_addr = server.recvfrom(1024)   # 接收任意客户端数据报
    current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    server.sendto(current_time.encode('utf-8'), client_addr)
    print(f'已响应 {client_addr}: {current_time}')
# udp_time_client.py
import socket

SERVER_HOST = '127.0.0.1'
SERVER_PORT = 12345

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.settimeout(3)   # 3秒超时,防止无限等待

client.sendto(b'', (SERVER_HOST, SERVER_PORT))   # 发送空数据报触发服务器响应
try:
    response, _ = client.recvfrom(1024)
    print(f'服务器时间: {response.decode("utf-8")}')
except socket.timeout:
    print('请求超时:请确认服务器已启动且地址正确')
finally:
    client.close()

运行与验证

  1. 启动服务器:在终端执行 python udp_time_server.py,看到监听提示后保持运行。
  2. 运行客户端:另开一个终端,执行 python udp_time_client.py。
  3. 判定通过条件:客户端打印出格式为 YYYY-MM-DD HH:MM:SS 的服务器当前时间,且服务器终端显示对应客户端的地址与响应时间,即表示UDP通信成功。

核心要点

  • SOCK_DGRAM 指定UDP协议,recvfrom 返回数据及发送方地址,sendto 需要显式指定目标地址——这正是无连接通信的体现。
  • 客户端发送空数据报 b'' 是合法的,UDP允许零长度数据报;服务器仅将其视为触发信号。
  • 若客户端先于服务器启动,数据报会丢失(UDP不保证送达),客户端将触发3秒超时——这是无连接协议的典型行为,可借此观察。