Few days ago I coded a mini game to learn socket and threading. When I run my game server and client on windows it just works perfectly but when I moved my server file to my test server it gives me this pickle error:
Exception in thread Thread-2: Traceback (most recent call last): File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner self.run() File "/usr/lib/python3.8/threading.py", line 870, in run self._target(*self._args, **self._kwargs) File "server.py", line 46, in handle_client obj = pickle.loads(conn.recv(obj_length)) _pickle.UnpicklingError: invalid load key, ' '.
What could be the problem?
Whole game files: ———————————————————————————
Codes that might help:
server.py:
import socket import threading import pickle import time import random import ast #------------------------------------------------------------------------- class Server(): def __init__(self): self.HEADER = 2048 self.PORT = 6000 self.SERVER = "ip" self.ADDR = (self.SERVER, self.PORT) self.FORMAT = 'utf-8' self.DISCONNECT_MESSAGE = "!DISCONNECT" self.ROLES = ["Mafya", "Mafya", "Köylü", "Doktor","Gözcü"] self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.bind(self.ADDR) #--------------------------------------------------------------------------------------------- self.names = list() self.addresses = list() self.lobby_dict = dict() self.game_dict = dict() self.ready_list = list() self.alive_list = list() self.vote = list() self.kill_vote = list() self.who_voted = list() self.ready_for_day = list() self.protected = None def handle_client(self, conn, addr): try: if addr[0] not in self.addresses: print(f"[NEW CONNECTION] {addr[0]} connected.") self.addresses.append(addr[0]) connected = True while connected: obj_length = conn.recv(self.HEADER).decode(self.FORMAT) if obj_length: obj_length = int(obj_length) obj = pickle.loads(conn.recv(obj_length)) if obj == self.DISCONNECT_MESSAGE: connected = False print(f"[DISCONNECTED] {addr[0]} disconnected.") elif "?ONLINE" in obj: lobby_id = obj.split(":")[1] conn.send(pickle.dumps(self.lobby_dict[lobby_id]["Players"])) elif "!NEWLOBBY" in obj: splitted_obj = obj.split(":") lobby_id = splitted_obj[1] admin = splitted_obj[2] capacity = splitted_obj[3] self.lobby_dict[lobby_id] = {"Players":[admin],"Capacity":capacity} elif "!JOINLOBBY" in obj: splitted_obj = obj.split(":") lobby_id = splitted_obj[1] name = splitted_obj[2] if lobby_id in self.lobby_dict.keys(): self.lobby_dict[lobby_id]["Players"].append(name) conn.send(pickle.dumps(f"True:{self.lobby_dict[lobby_id]['Capacity']}")) else: conn.send(pickle.dumps("False")) elif "?ALIVE" in obj: conn.send(pickle.dumps(self.alive_list)) #----------------------------------------------------------------------------------------------- #Game commands: elif "!NAME" in obj: name = obj.split(":")[1] self.names.append(name) elif "!ALIVE" in obj: conn.send(pickle.dumps(self.names)) elif "!READY" in obj: ready_player = obj.split(":")[1] self.ready_list.append(ready_player) elif "!SHUFFLE" in obj: if len(self.ready_list) == len(self.names): temp = self.ROLES if len(self.names) > len(self.ROLES): for i in range(0,len(self.names) - len(self.ROLES)): temp.append("Köylü") random.shuffle(temp) for i in range(len(self.names)): self.game_dict[self.names[i]] = temp[i] conn.send(pickle.dumps(f"True/{self.game_dict}")) with open("shuffled_roles.txt", "w", encoding="utf-8") as file: file.write(str(self.game_dict)) print(f"[SHUFFLED LIST] {self.game_dict}") else: conn.send(pickle.dumps("False")) elif "!ROLES" in obj: if len(self.ready_list) == len(self.names): with open("shuffled_roles.txt", "r", encoding="utf-8") as file: line = file.readline() self.game_dict = ast.literal_eval(line) conn.send(pickle.dumps(f"True/{self.game_dict}")) else: conn.send(pickle.dumps("False")) elif "!VOTE" in obj: voted_player = obj.split(":")[1] who = obj.split(":")[2] + ": " + voted_player self.who_voted.append(who) self.vote.append(voted_player) elif "!VRESULTS" in obj: conn.send(pickle.dumps(self.vote)) conn.send(pickle.dumps(self.who_voted)) elif "!VCLEAN" in obj: self.vote = [] self.who_voted = [] elif "!PROTECTED" in obj: protected = obj.split(":")[1] self.protected = obj elif "!NIGHT_KILL" in obj: kill = obj.split(":")[1] self.kill_vote.append(kill) elif "!NKRESULTS" in obj: nk_results = self.kill_vote nk_protected = self.protected if len(nk_results) == 1: if nk_results[0] != nk_protected: conn.send(pickle.dumps(nk_results[0])) elif len(nk_results) == 2: if nk_results[0] == nk_results[1]: if nk_results[0] != nk_protected and nk_results[0] != "None": conn.send(pickle.dumps(nk_results[0])) elif nk_results[0] == "None" and nk_results[1] != "None": conn.send(pickle.dumps(nk_results[1])) elif nk_results[1] == "None" and nk_results[0] != "None": conn.send(pickle.dumps(nk_results[0])) else: conn.send(pickle.dumps(None)) else: conn.send(pickle.dumps(None)) elif "!NKCLEAN" in obj: self.protected = "None" self.kill_vote = [] elif "!RFORDAY" in obj: rplayer = obj.split(":")[1] self.ready_for_day.append(rplayer) elif "!RFDLIST" in obj: conn.send(pickle.dumps(self.ready_for_day)) elif "!RFDCLEAN" in obj: self.ready_for_day = list() else: print(f"[{addr}] {obj}") #İsimler Buradan -> addr except ConnectionResetError: print(f"[CONNECTION] {addr} Connection reset exception has been handled.") finally: conn.close() def start(self): print("[STARTING] Server is starting...") self.server.listen() print("[LISTENING] Server is listening on {}".format(self.SERVER)) while True: conn, addr = self.server.accept() thread = threading.Thread(target=self.handle_client, args=(conn, addr)) thread.start() if __name__ == "__main__": server = Server() server.start()
app.py:
import sys import os import time import random import socket import threading import pickle class Config(): def __init__(self): self.HEADER = 2048 self.PORT = 6000 self.SERVER = "ip" #socket.gethostbyname(socket.gethostname()) self.ADDR = (self.SERVER, self.PORT) self.FORMAT = 'utf-8' self.DISCONNECT_MESSAGE = "!DISCONNECT" self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.menuText = """ ░██╗░░░░░░░██╗██╗░██████╗███████╗ ████████╗░█████╗░░██╗░░░░░░░██╗███╗░░██╗ ░██║░░██╗░░██║██║██╔════╝██╔════╝ ╚══██╔══╝██╔══██╗░██║░░██╗░░██║████╗░██║ ░╚██╗████╗██╔╝██║╚█████╗░█████╗░░ ░░░██║░░░██║░░██║░╚██╗████╗██╔╝██╔██╗██║ ░░████╔═████║░██║░╚═══██╗██╔══╝░░ ░░░██║░░░██║░░██║░░████╔═████║░██║╚████║ ░░╚██╔╝░╚██╔╝░██║██████╔╝███████╗ ░░░██║░░░╚█████╔╝░░╚██╔╝░╚██╔╝░██║░╚███║ ░░░╚═╝░░░╚═╝░░╚═╝╚═════╝░╚══════╝ ░░░╚═╝░░░░╚════╝░░░░╚═╝░░░╚═╝░░╚═╝░░╚══╝ """ class Client(Config): def __init__(self): super().__init__() self.client.connect(self.ADDR) self.admin = None def send(self, obj): obj = pickle.dumps(obj) obj_length = len(obj) send_length = str(obj_length).encode(self.FORMAT) send_length += b' ' * (self.HEADER - len(send_length)) self.client.send(send_length) self.client.send(obj) def messenger(self): while True: msg = input("Mesaj: ") self.send(msg) class Menu(Client): def __init__(self): super().__init__() class MainMenu(Menu): def __init__(self, player_name): super().__init__() self.player_name = player_name def printMainMenu(self, game_is_on = False): print("n" * 7 + self.menuText) time.sleep(2) os.system("cls") print(self.menuText) print(""" 1. Yeni Oyun Oluştur 2. Oyuna Katıl """) class Lobby(Menu): def __init__(self): super().__init__() def lobbyMenu(self): name_send = f"!NAME:{self.player_name}" self.send(name_send) os.system("cls") print(self.menuText) print(f"Lobby ID: {self.id}") self.send(f"?ONLINE:{self.id}") self.online_list = pickle.loads(self.client.recv(2048)) temp_list = self.online_list sys.stdout.write("Aktif Oyuncular:| ") for i in self.online_list: sys.stdout.write(f"{i} | ") sys.stdout.flush() while not self.game_started: time.sleep(1) self.send("?ONLINE:" + str(self.id)) self.online_list = pickle.loads(self.client.recv(2048)) if temp_list != self.online_list: sys.stdout.write("rAktif Oyuncular:| ") for i in self.online_list: sys.stdout.write(f"{i} | ") sys.stdout.flush() temp_list = self.online_list if len(self.online_list) == self.capacity: self.game_started = True class CreateLobby(Lobby): def __init__(self, capacity, player_name, admin): super().__init__() self.player_name = player_name self.admin = admin self.id = random.randint(100000,999999) self.capacity = int(capacity) self.game_started = False self.send(f"!NEWLOBBY:{self.id}:{self.player_name}:{self.capacity}") self.lobbyMenu() class JoinLobby(Lobby): def __init__(self, id, player_name): super().__init__() self.id = id self.player_name = player_name self.game_started = False self.lobby_joiner() def lobby_joiner(self): self.send(f"!JOINLOBBY:{self.id}:{self.player_name}") bool_obj = pickle.loads(self.client.recv(2048)) while bool_obj == "False": print("Bu ID'ye ait lobby bulunmamaktadır.") self.id = input(" Lobby ID: ") self.send(f"!JOINLOBBY:{self.id}:{self.player_name}") bool_obj = pickle.loads(self.client.recv(2048)) self.capacity = int(bool_obj.split(":")[1]) self.lobbyMenu() #-------------------------------------------------------------------------------------------------------------
Advertisement
Answer
Your code includes
obj = pickle.loads(conn.recv(obj_length))
The problem is that TCP is a streaming protocol and the entire obj_length
of data may not have been received when the call is made. When you run client and server on the same machine, you don’t have the real network with real segmentation and delays, so you don’t see the problem.
The solution is your own receiver that knows to keep asking for data until it sees it all
def recvall(conn, count): recvlist = [] recvcount = 0 while recvcount < count: buf = conn.recv(count-recvcount) if not buf: # replace with your error handling here raise OSError("Connection terminated") recvlist.append(buf) recvcount += len(buf) return b"".join(recvlist)
Replace your original line with
obj = pickle.loads(recvall(conn, obj_length))
and it should work