Python Socket通信异常排查:recvTillTimeOut函数抛出异常
Socket服务端-客户端数据接收异常问题排查与修复
问题描述
我是Python新手,正在开发Socket服务端-客户端程序,希望实现发送方发送消息时,接收方持续监听直至传输完成。为此编写了recvTillTimeOut函数,预期通过超时机制完成数据接收,但每次连接建立后,该函数都会抛出异常,无法正常运行。
原recvTillTimeOut函数代码
def recvTillTimeOut(s): s.settimeout(1) data = b'' while True: try: data += s.recv(1024) print(data) except: print("error") break return data
完整原代码
服务端代码
import socket import pickle def recvTillTimeOut(s): s.settimeout(1) data = b'' while True: try: data += s.recv(1024) print(data) except: print("error") break return data PublicKey = [2342314, 422421] mySocket = socket.socket() host = socket.gethostname() portNum = 12345 mySocket.bind((host, portNum)) mySocket.listen(2) clientConn, clientAddr = mySocket.accept() try: data = recvTillTimeOut(mySocket) clientkey = pickle.loads(data) print(clientkey) data = pickle.dumps(PublicKey) mySocket.send(data) while True: data = clientConn.recv(1024).decode() if not data: break print("from connected user: " + str(data)) data = input(" -> ") clientConn.send(data.encode()) except: mySocket.close() mySocket.close()
客户端代码
import socket import pickle def recvTillTimeOut(s): s.settimeout(1) data = b'' while True: try: data += s.recv(1024) except: break return data PublicKey = [100123120, 5124210] host = socket.gethostname() portNum = 12345 mySocket = socket.socket() mySocket.connect((host, portNum)) try: data = pickle.dumps(PublicKey) mySocket.send(data) data = recvTillTimeOut(mySocket) serverkey = pickle.loads(data) print(serverkey) while True: message = input(" -> ") if message == 'bye': break mySocket.send(message.encode()) data = mySocket.recv(1024).decode() print('Received from server: ' + data) except: mySocket.close() mySocket.close()
问题根源
- 服务端使用错误的Socket对象:服务端调用
recvTillTimeOut时传入的是监听Socket(mySocket),但监听Socket只能用于接受新连接,不能直接收发数据,这是导致异常的核心原因。后续发送公钥时同样错误使用了mySocket.send,应该用clientConn(已建立连接的客户端Socket)。 - 异常捕获过于笼统:
except捕获所有异常,无法区分是超时还是连接断开等其他错误,不利于排查问题。 - 依赖超时判断传输完成不可靠:超时时间设置过短可能导致数据未接收完整就停止,过长则会造成不必要的等待,且无法准确判断数据是否传输完毕。
修复方案
1. 修正recvTillTimeOut函数
明确捕获超时异常,恢复原Socket超时设置,避免影响后续操作:
import socket def recvTillTimeOut(s): # 保存原超时设置 original_timeout = s.gettimeout() s.settimeout(1) data = b'' while True: try: chunk = s.recv(1024) if not chunk: # 客户端关闭连接,终止接收 break data += chunk except socket.timeout: # 超时判定为当前无数据,结束接收 break except Exception as e: print(f"接收异常: {str(e)}") break # 恢复原超时配置 s.settimeout(original_timeout) return data
2. 修正服务端核心逻辑
将所有收发数据的操作切换到已建立连接的clientConn对象:
try: # 用clientConn接收客户端公钥 data = recvTillTimeOut(clientConn) clientkey = pickle.loads(data) print(clientkey) # 用clientConn发送服务端公钥 data = pickle.dumps(PublicKey) clientConn.send(data) while True: data = clientConn.recv(1024).decode() if not data: break print("from connected user: " + str(data)) data = input(" -> ") clientConn.send(data.encode()) except Exception as e: print(f"服务端错误: {str(e)}") mySocket.close()
3. 更可靠的传输方案(可选)
依赖超时判断传输完成不够稳定,建议采用先发送数据长度,再发送数据的方式,确保接收方准确获取完整数据:
客户端发送数据修改
data = pickle.dumps(PublicKey) # 先发送4字节的长度信息(大端字节序) mySocket.send(len(data).to_bytes(4, byteorder='big')) # 再发送实际数据 mySocket.send(data)
对应接收函数
def recv_exact(s, expected_len): """接收指定长度的字节数据""" received = b'' while len(received) < expected_len: chunk = s.recv(min(expected_len - len(received), 1024)) if not chunk: raise ConnectionError("连接已中断") received += chunk return received def recv_complete(s): """先接收数据长度,再接收完整数据""" len_bytes = recv_exact(s, 4) data_len = int.from_bytes(len_bytes, byteorder='big') return recv_exact(s, data_len)
使用时直接调用recv_complete(clientConn)即可准确接收完整的pickle数据,无需依赖超时。
内容的提问来源于stack exchange,提问作者abdo Salm
相关产品推荐
相关产品推荐

