Python Socket并行收发问题:客户端无法正确匹配CPU与内存使用率数据
问题分析与解决方案
你的问题本质上是TCP字节流特性加上无请求的盲目推送导致的数据匹配混乱。咱们拆解一下原因:
- TCP是字节流协议:它没有内置的消息边界,服务器两个线程同时调用
send()时,数据可能会被操作系统合并发送,客户端的recv()根本分不清哪段是CPU数据、哪段是内存数据。 - 服务器逻辑错误:你的服务器完全忽略了客户端发送的"cpu"/"ram"请求,只是在两个线程里自顾自地循环发送数据,相当于服务器在乱泼数据,客户端两个线程随机接,自然会串。
要实现你想要的「服务器只发原始数据,客户端负责匹配区分」的需求,核心是改成请求-响应模式:客户端明确请求某类数据,服务器只返回对应的数据,这样每个响应都能精准匹配请求。
修改后的服务器代码
import socket import psutil server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(("127.0.0.1", 6767)) server.listen() print("Waiting for client connection...") client, address = server.accept() print(f"Connected to {address}") try: while True: # 接收客户端的请求指令,去掉多余空白字符 request = client.recv(1024).decode("ascii").strip() if not request: break # 客户端断开连接 # 根据请求返回对应原始数据 if request == "cpu": # cpu_percent(1)会阻塞1秒采样,确保数据准确 cpu_usage = str(psutil.cpu_percent(1)) client.send(cpu_usage.encode("ascii")) elif request == "ram": ram_usage = str(psutil.virtual_memory()[2]) client.send(ram_usage.encode("ascii")) else: client.send("Invalid request".encode("ascii")) except Exception as e: print(f"Connection error: {e}") finally: client.close() server.close()
修改后的客户端代码
import socket import threading import time client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("127.0.0.1", 6767)) def fetch_cpu(): while True: # 发送CPU请求 client.send("cpu".encode("ascii")) # 接收对应的CPU数据 cpu_usage = client.recv(1024).decode("ascii") print(f"cpu usage is {cpu_usage}") time.sleep(1) def fetch_ram(): while True: # 发送内存请求 client.send("ram".encode("ascii")) # 接收对应的内存数据 ram_usage = client.recv(1024).decode("ascii") print(f"ram usage is {ram_usage}") time.sleep(1) # 启动两个线程分别获取数据 threading.Thread(target=fetch_cpu).start() threading.Thread(target=fetch_ram).start()
额外优化:解决潜在的粘包问题
虽然上面的代码在大多数情况下能正常工作,但如果客户端请求频率很高,TCP可能会把多个响应粘在一起发送。可以给每个数据加个消息边界(比如换行符),确保客户端能准确拆分数据:
服务器端修改发送逻辑
把client.send(...)改成:
client.send(f"{cpu_usage}\n".encode("ascii")) # CPU数据加换行 # 内存数据同理 client.send(f"{ram_usage}\n".encode("ascii"))
客户端新增按行读取的函数
def recv_line(sock): buffer = b"" while True: chunk = sock.recv(1) if not chunk or chunk == b"\n": break buffer += chunk return buffer.decode("ascii")
然后在fetch_cpu和fetch_ram里用这个函数替代recv(1024):
cpu_usage = recv_line(client)
这样无论数据是否粘包,都能准确读取到完整的单个指标数据。
内容的提问来源于stack exchange,提问作者Morteza
相关产品推荐
相关产品推荐

