Python Socket连接偶发数据接收不全问题求助
网络环境下Socket消息接收不完整导致Pickle反序列化失败问题
我编写了一个支持多客户端连接的服务器测试程序,服务器会循环向客户端发送加密后的自身时间信息。本地localhost运行时一切正常,但用连接手机热点的笔记本客户端连接远程服务器时,短时间内可以正常收发消息,但会在随机时间(5-60秒左右)出现客户端或服务器接收消息不完整的情况,最终导致pickle反序列化不完整数据时抛出错误。
server.py
import datetime import logging import socket import threading import traceback import rsa.randnum from blob_api import * logging.basicConfig(format='%(levelname)s - %(message)s', level=logging.DEBUG) SERVER_IP = "192.168.101.1" SERVER_PORT = 8108 class __Server(socket.socket): def __init__(self): super().__init__() self.public_key, self.private_key = rsa.newkeys(2048) try: self.bind((SERVER_IP, SERVER_PORT)) except socket.error as _: logging.error(traceback.format_exc()) self.listen() logging.info(f"Server started: ('{SERVER_IP}', {SERVER_PORT}).") while True: client_socket, client_address = self.accept() logging.info(f"Connected to client: {client_address}, waiting for public key...") threading.Thread(target=self.client_thread, args=(client_socket, client_address,)).start() def client_thread(self, client_socket: socket.socket, client_address): client_public_key: rsa.PublicKey = pickle.loads(client_socket.recv(2048)) client_socket.sendall(pickle.dumps(self.public_key)) logging.info(f"Established connection to client: {client_address}.") n = 0 while True: n += 1 try: header = client_socket.recv(HEADER_SIZE) print(f"Header recv {n}: " + header.decode()) if not len(header): break packet = client_socket.recv(int(header.decode())) data = decrypt(packet, self.private_key) client_socket.sendall(create_message(encrypt(f"Server {n}: {datetime.datetime.now()}", client_public_key))) except: logging.error(traceback.format_exc()) print(packet) break logging.warning(f"Lost connection to client: {client_address}") client_socket.close() __Server()
client.py
import datetime import socket import sys import threading import logging import traceback from blob_api import * logging.basicConfig(format='%(levelname)s - %(message)s', level=logging.DEBUG) SERVER_IP = "SERVER_PUBLIC_IP" SERVER_PORT = 8108 class Connection(socket.socket): def __init__(self): super().__init__() self.public_key, self.private_key = rsa.newkeys(2048) try: self.connect((SERVER_IP, SERVER_PORT)) except ConnectionRefusedError as e: logging.warning("Could not connect to server " + str(e)) sys.exit() self.sendall(pickle.dumps(self.public_key)) self.server_public_key = pickle.loads(self.recv(2048)) threading.Thread(target=self.__handle_requests).start() def __handle_requests(self): logging.info("Established connection to server.") n = 0 while True: n += 1 try: message = create_message(encrypt(f"Client: {datetime.datetime.now()}", self.server_public_key)) print(message) self.sendall(message) header = self.recv(HEADER_SIZE) print(f"Header recv {n}: " + header.decode()) if not len(header): break packet = self.recv(int(header.decode())) data = decrypt(packet, self.private_key) print(data) except: logging.error(traceback.format_exc()) break logging.warning("Lost connection to server.") self.close() Connection()
blob_api.py
import pickle import obj_encrypt import rsa import rsa.randnum HEADER_SIZE = 8 def encrypt(data: any, public_key: rsa.PublicKey) -> bytes: secret = obj_encrypt.Secret(key=str(rsa.randnum.read_random_int(32))) return pickle.dumps({ "data": secret.encrypt(data), "secret": rsa.encrypt(pickle.dumps(secret), public_key) }) def decrypt(packet: bytes, private_key: rsa.PrivateKey) -> any: packet = pickle.loads(packet) return pickle.loads(rsa.decrypt(packet["secret"], private_key)).decrypt(packet["data"]) def create_message(message: bytes) -> bytes: return f"{len(message):<8}".encode() + message
客户端错误输出
Header recv 44: 349 Server 44: 2024-03-13 12:29:40.200717 Len: 346 b"346 \x80\x04\x95O\x01\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04data\x94C1\x00\x9f\xc0\xe9]\xea\x9c\tg#\xa0\x83;\x9d\x1a\xda\xe3\xb5\xa9u\x7f\x15\xff\xeah\x0c\xf8f\xbe\xc14\x8c\xa2\xc1T^\xf9Z]\x16\xf2\x96,3\xd6\\\xb0\x04[\x94\x8c\x06secret\x94B\x00\x01\x00\x00a`\x05#\xf7\x08}\x17Ay\xe2\x96\xc8\xab:V@%\x94?j-\xd3F\xd3Q\xc1\xab\xf3\x94\x9b\xc3U\xc2\xe7fD\n\x17\xad2\x0c\xea\x87m\xb3\xfb\xffq\xbb'\xdd\x07\x0f\r\x95\x14]3\xdfuh\xf3\x03i\x9f\x94R\x92>\xce\xa5\x8d\xb0';\x1a\x91)kJ\x9f?\xbe\xec\xc4\x85\\\xed\xb2\x88]k\x7f\xfc~\x0e9\xcd\xa2P:\x86Nq,J\xe3ol\xe3\x00\x94\xf8^+j\xb2\x8d3\xb1\x84\t\x80a\x16\x98\x84\x0fM\xe9\xd7\xa9OZ\xcf\xab&\xe1\xab\xa5\x1b\xc9\r,\xaf\r\x1f0,\xc5\xd9\x11L\xe6>\xcb\x12\xe6\x9e\xbc\xde\xb48\x1f\xa9\xd2g\xb2BZ\x98\xa4\x00\x85\xa1\xc9\xba\x87\r\xe0\x16\xc2\x9d\x00\xc3lz\xc9\x0f\xb8\x1fE\xf0[\xd3N\xd4\xa1\x91e_IS\x17C\x014V\xce@\xa3\x04.\xbc\xd0\x05\xb3\xf1\xc6\x97\x04_\xa6W\xe0\x17\x82\xa0\\\x92P3g\xd9\x1e.\x82{?'4\xd2\x19l\xce\xb0\xb5\xb0\xc3M\x8d\x14%V\x94u." Header recv 45: 349 Server 45: 2024-03-13 12:29:40.292893 Len: 346 b'346 \x80\x04\x95O\x01\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04data\x94C1\x89\xbe"\xdb\xd5=\x19\x01\xc8\xc3\xba\x81\x96\x07L)\xfa,\'(\xaa\x80"\xadt\xbck\x8fZY\x8c\xd2\x84\x0e\xfc\xdb$\x1dY\xf3\x98\x8e\x88pvC\xd9\xb2\x8b\x94\x8c\x06secret\x94B\x00\x01\x00\x00B\xce\x93\xa8\xbbf\n\xc5\xcbi6\xfe}\xff\xb1\x8e\xc8\x98\xbb\x8bbp\\g\x0f\x15y\xf6\xf0_\x0e\x82\x8b\x1e\xb3\xb3\xdf\x17\xc6\x97\xd9\x02\x88\x00l\xaa={gF\x92\xec;\xdbB\x08\xbd\x18I\x84*\xb9\xb8P\xbf\r|\xb2\xbeP\x19\xb1+\xb2\xaa[\xd8\xc7\x1e\xb4K(\x05\xbd_\x90\x99\xdd.\xfb^\x17\xac\xa7\xd69\x13\x8c\x1e\x8e\xb4I\xbc\xb2)c\xc5\x97\xb5\xb3e2\x91\xb2\xd8\xe3\xc1\xbe\x0c\xdcd\xd0\xf3\x84\xfa[\xfc6\xbd_\x12M\xafS\xcc\xe4H\x8bQQ\xac\x07\xff\xe1\xd1\x19IW\xf6\x94\xf3\xd13\x08F\xe2\x07 U<\xcb\xecD\xcd\x84\x00#\xe4\xc2\xa5(\x80\x12\x80\x1dm\xab\x8b\x19N\xa1;/\x13\x7f\x05\xde\xc4+\xdeKQ\x01\'\xcc\xe6;>s\x01\xf9\xdd\x80\xdc\xb0A\xee\xba\x85c/\x7f\x04z\xe0H\xb6l\x90\x9a\x91\xa5\xed\xe28\xa8T\xf4\x99l\xd6\xf2\x85\xcb\x05\xee\x19,o\xa7\xe1w\x80\x87.\x02@n#\xac\xadQ\xa2\xb6\x94u.' Header recv 46: WARNING - Lost connection to server.
服务器错误输出
Header recv 44: 346 Len: 349 Header recv 45: 346 Len: 349 Header recv 46: 346 ERROR - Traceback (most recent call last): File "/path_to/server.py", line 46, in client_thread data = decrypt(packet, self.private_key) File "/path_to/blob_api.py", line 18, in decrypt packet = pickle.loads(packet) _pickle.UnpicklingError: pickle data was truncated b'\x80\x04\x95O\x01\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04data\x94C1\x89\xbe"\xdb\xd5=\x19\x01\xc8\xc3\xba\x81\x96\x07L)\xfa,\'(\xaa\x80"\xadt\xbck\x8fZY\x8c\xd2\x84\x0e\xfc\xdb$\x1dY\xf3\x98\x8e\x88pvC\xd9\xb2\x8b\x94\x8c\x06secret\x94B\x00\x01\x00\x00B\xce\x93\xa8\xbbf\n\xc5\xcbi6\xfe}\xff\xb1\x8e\xc8\x98\xbb\x8bbp\\g\x0f\x15y\xf6\xf0_\x0e\x82\x8b\x1e\xb3\xb3\xdf\x17\xc6\x97\xd9\x02\x88\x00l\xaa={g'
客户端向服务器发送了346字节的消息,但服务器仅收到137字节,此前已正常运行45次。怀疑该问题与手机热点的网络连接有关,但不确定具体原因,寻求解决办法。
内容的提问来源于stack exchange,提问作者Sam Walker
相关产品推荐
相关产品推荐

