TCP客户端未等待recv直接发送全部数据的问题求助
TCP客户端服务端时序问题分析与修复方案
核心问题定位
- 代码缩进错误:客户端代码里的
sock.connect及后续收发逻辑没正确缩进在with代码块内,导致这些代码根本没在socket上下文里执行,相当于直接跳过所有recv操作,一次性发送了全部数据。 - 收发时序不匹配:服务端的步骤和客户端的接收/发送动作没有严格对应,加上未校验
recv返回值,无法确保服务端响应已被正确接收。 - 未处理流式协议边界:TCP是流式协议,
recv(1024)不一定能拿到完整消息,当前代码没做消息完整性校验,容易出现解析错误。
修复步骤
1. 修正客户端代码(缩进+校验)
确保所有socket操作都在with块内,阻塞模式下recv才会生效,同时添加响应校验:
freq = 8 key = 1 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((self.HOST, self.PORT)) progress_callback.emit(1) # 初始化消息:发→收 sock.sendall(bytes(TcpMsgs.INIT_MSG.value, "UTF-8")) resp = str(sock.recv(1024)).strip().lower() # 校验服务端返回的空格响应 if resp != " ": raise RuntimeError("初始化响应异常") progress_callback.emit(2) # 频率消息:发→收 sock.sendall(bytes(TcpMsgs.FREQ_MSG.value + str(freq), "UTF-8")) resp = str(sock.recv(1024)).strip().lower() if resp != "ok": raise RuntimeError("频率设置响应异常") progress_callback.emit(3) # 密钥长度消息:发→收 sock.sendall(bytes(TcpMsgs.KEY_LEN_MSG.value + str(key), "UTF-8")) progress_callback.emit(4) # Beacon消息:发→收 sock.sendall(bytes(TcpMsgs.BEACON_MSG.value, "UTF-8")) resp = str(sock.recv(1024)).strip().lower() if resp != "ok": raise RuntimeError("Beacon响应异常") progress_callback.emit(5) # 等待Beacon确认响应 resp = str(sock.recv(1024)).strip().lower() if resp != "ok": raise RuntimeError("Beacon确认响应异常") # 接收服务端最终的随机数组 rand_data = sock.recv(1024) rand_array = pickle.loads(rand_data)
2. 修正服务端代码(时序+异常处理)
严格对应客户端的收发步骤,同时处理recv返回空(客户端断开)的情况:
import pickle import random with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: print("OPENING SOCKET") s.bind(("", self.__port)) s.listen() conn, addr = s.accept() with conn: progress_callback.emit(1) print("Connected by:", addr) # 接收初始化消息 init_msg = conn.recv(1024) if not init_msg: print("客户端在初始化阶段断开") break progress_callback.emit(2) conn.sendall(bytes(" ", "UTF-8")) # 接收频率消息并回复 freq_msg = conn.recv(1024) if not freq_msg: break freq = float(str(freq_msg).split(":")[1]) progress_callback.emit(3) self.__dataDictionary.update({BobVars.FREQ_DICT_KEY.value: freq}) conn.sendall(bytes("ok", "UTF-8")) # 接收密钥长度消息 key_len_msg = conn.recv(1024) if not key_len_msg: break key_len = int(str(key_len_msg).split(":")[1]) progress_callback.emit(4) self.__dataDictionary.update({BobVars.KEY_LEN_DICT_KEY.value: key_len}) # 接收Beacon消息并回复 beacon_msg = conn.recv(1024) if not beacon_msg: break progress_callback.emit(5) conn.sendall(bytes("ok", "UTF-8")) # 等待内部Beacon确认 while self.__waitFlag: pass progress_callback.emit(6) # 发送Beacon确认响应 conn.sendall(bytes("ok", "UTF-8")) self.__waitFlag = not self.__waitFlag # 发送随机数组 rand_array = pickle.dumps([random.randint(0,1) for _ in range(key_len)]) conn.sendall(rand_array)
3. 额外优化建议
- 消息边界处理:TCP是流式协议,建议给每条消息加长度前缀或结束标记(比如换行符),循环接收直到拿到完整消息,避免因消息拆分导致解析错误。
- 异常捕获:在客户端和服务端添加
try-except块,捕获ConnectionResetError、TimeoutError等异常,避免程序直接崩溃。 - 阻塞模式验证:如果修复后还是出现跳过
recv的情况,检查是否有其他线程干扰progress_callback的执行,导致代码逻辑异步执行。
内容的提问来源于stack exchange,提问作者Guillermo Fuentes
相关产品推荐
相关产品推荐

