架构图
交互泳道图
以下是一个完整的 UDP 客户端与服务端 示例,完全满足以下需求:
✅ 功能说明
| 功能 | 描述 |
| UDP 通信 | 使用 socket.SOCK_DGRAM 实现无连接 UDP 通信 |
| JSON 格式 | 请求与响应均使用 JSON 格式传输 |
| 客户端多线程 | 同时启动多个客户端线程发送请求 |
| UUID 生成 | 客户端随机生成 search_id 字段 |
| 服务端线程池 | 使用 ThreadPoolExecutor 并发处理请求 |
| 客户端超时设置 | 客户端设置响应等待超时(默认 5 秒) |
| search_id 校验 | 客户端验证响应中的 search_id 是否匹配请求 |
🧩 服务端代码(支持线程池)
server端代码
import socket import json import uuid from concurrent.futures import ThreadPoolExecutor # 服务端配置 HOST = '127.0.0.1' PORT = 9999 MAX_BUFFER_SIZE = 4096 MAX_THREADS = 500 # 最大并发线程数 # 创建 UDP socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind((HOST, PORT)) print(f"UDP 服务端启动,监听地址 {HOST}:{PORT}") # 使用线程池处理请求 executor = ThreadPoolExecutor(max_workers=MAX_THREADS) def handle_request(data, client_address): try: # 解析 JSON 请求 request = json.loads(data.decode('utf-8')) search_id = request.get('search_id') message = request.get('message', '') print(f"[请求] 来自 {client_address},search_id: {search_id}") # 构造响应 response = { "status": "success", "search_id": search_id, "message": f"已收到请求: {message}" } json_response = json.dumps(response).encode('utf-8') server_socket.sendto(json_response, client_address) print(f"[响应] 发送至 {client_address},search_id: {search_id}") except json.JSONDecodeError: print(f"[错误] 接收的不是有效的 JSON 数据,来自 {client_address}") except Exception as e: print(f"[异常] 处理请求时发生错误: {e}") # 主循环接收请求 try: while True: data, client_address = server_socket.recvfrom(MAX_BUFFER_SIZE) executor.submit(handle_request, data, client_address) except KeyboardInterrupt: print("\n服务端关闭。") server_socket.close()
📡 客户端代码(支持多线程 + UUID + 超时 + search_id 校验)
client 端代码
import socket import json import uuid import threading import time import random # 客户端配置 SERVER_HOST = '127.0.0.1' SERVER_PORT = 9999 CLIENT_THREADS = 500 # 客户端线程数 TIMEOUT = 5 # 超时时间(秒) MAX_BUFFER_SIZE = 32000 def client_thread_func(thread_id): # 每个线程使用自己的 socket client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.settimeout(TIMEOUT) # 生成随机 UUID 作为 search_id search_id = str(uuid.uuid4()) request = { "search_id": search_id, "message": f"这是来自线程 {thread_id} 的请求" } json_request = json.dumps(request).encode("utf-8") t1 = time.time() print(f"[客户端线程 {thread_id}] 发送请求,search_id: {search_id}") client_socket.sendto(json_request, (SERVER_HOST, SERVER_PORT)) try: # 等待响应 response_data, server_address = client_socket.recvfrom(MAX_BUFFER_SIZE) response = json.loads(response_data) print(f"[客户端线程 {thread_id}] 收到响应: {response}") t2 = time.time() time_use_ms = round(1000*(t2-t1), 3) # 校验 search_id if 'search_id' not in response: print(f"[客户端线程 {thread_id}] 响应中缺少 search_id 字段! time_use_ms: {time_use_ms}") elif response['search_id'] != search_id: print(f"[客户端线程 {thread_id}] 错误!search_id 不匹配。发送的: {search_id},返回的: {response['search_id']}. time_use_ms: {time_use_ms}") else: print(f"[客户端线程 {thread_id}] search_id 校验通过。 time_use_ms: {time_use_ms}") except socket.timeout: print(f"[客户端线程 {thread_id}] 超时,未收到响应。") except json.JSONDecodeError: print(f"[客户端线程 {thread_id}] 接收到的响应不是有效的 JSON 数据。") except Exception as e: print(f"[客户端线程 {thread_id}] 发生异常: {e}") finally: client_socket.close() # 启动多个客户端线程 threads = [] for i in range(CLIENT_THREADS): t = threading.Thread(target=client_thread_func, args=(i,)) threads.append(t) t.start() # 随机延迟 time.sleep(random.uniform(0.0001, 0.001)) # 等待所有线程完成 for t in threads: t.join() print("所有客户端线程执行完毕。")
🧪 运行方式
- 先启动服务端:
- bash
- 深色版本
python server.py
- 再运行客户端:
- bash
- 深色版本
python client.py
📌 示例输出
服务端输出(线程池处理):
深色版本
UDP 服务端启动,监听地址 127.0.0.1:9999 [请求] 来自 ('127.0.0.1', 65432),search_id: 123e4567-e89b-12d3-a456-426614174000 [响应] 发送至 ('127.0.0.1', 65432),search_id: 123e4567-e89b-12d3-a456-426614174000
客户端输出(正常响应):
深色版本
[客户端线程 0] 发送请求,search_id: a1b2c3d4-e5f6-7890-abcd-1234567890ab [客户端线程 0] 收到响应: {'status': 'success', 'search_id': 'a1b2c3d4-e5f6-7890-abcd-1234567890ab', 'message': '已收到请求: 这是来自线程 0 的请求'} [客户端线程 0] search_id 校验通过。
客户端输出(超时场景):
深色版本
[客户端线程 1] 发送请求,search_id: 98765432-1098-7654-3210-09876543210a [客户端线程 1] 超时,未收到响应。
🧠 技术要点说明
| 组件 | 说明 |
| 服务端线程池 | 使用 ThreadPoolExecutor 并发处理请求,避免阻塞主线程 |
| 客户端多线程 | 每个线程使用独立 socket,避免通信冲突 |
| UUID 生成 | 使用 uuid.uuid4() 生成唯一标识 |
| JSON 格式 | 使用 json.dumps() 和 json.loads() 实现序列化/反序列化 |
| 客户端超时 | 使用 socket.settimeout() 设置响应等待时间 |
| search_id 校验 | 客户端验证响应中的 search_id 是否与请求一致 |
🔒 注意事项
- 防火墙/网络环境:确保本地网络允许 UDP 通信(尤其在跨设备通信时)。
- 数据大小限制:建议控制在 1472 字节以内以避免分片。
- 线程池大小:根据实际负载调整
MAX_THREADS。 - UUID 唯一性:
uuid.uuid4()生成的 UUID 是全局唯一的。