Skip to content
Advertisement

Why python pickle does not work dos to unix?

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

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement