0

I'm pretty new to python and I'm trying to build a multiplayer tic tac toe game where 2 players can play at once. I have used sockets, select and threads to accomplish this.

However, I am at a dead end at a particular problem.

SERVER

    def incoming_message(self, client_socket: socket.socket, client_address):
        while True:
            try:
                client_msg = client_socket.recv(2048)
                move = client_msg.decode('utf-8')
                print(f"Player {self.players[f'{client_address}'].symbol} chose: {move}")
                self.send_board()
                self.send_updates(client_address)
            
            except Exception:
                import traceback
                print(traceback.format_exc())
                socket.close()

The above function is in its own thread. If it receives a 'message' or rather, a move from the player, it will try to send the players an updated board as well as what position the player had chosen.

    def send_updates(self, client_address):
        self.connected_clients[f"{client_address}"].send(
            bytes(f"You {self.players[f'{client_address}'].symbol} sent ", encoding='utf-8'))

    def send_board(self):
        for all_connected_clients in self.connected_clients:
            self.connected_clients[all_connected_clients].send(pickle.dumps(self.board))

send_board function sends the player the updated board using pickle as the board itself is a 2d array which I would format it on the client side.

send_updates is just a regular str that is being sent over as well.

CLIENT

    def incoming_message(self, client_socket):
        while True:
            ready_sockets, _, _ = select.select(
                [client_socket], [], [], 60
            )
            if ready_sockets:
                try:
                    for sockets in ready_sockets:
                        received_board = sockets.recv(1024)
                        self.board = pickle.loads(received_board)
                        self.print_board()
                                            
                except Exception as e:
                    print(sockets.recv(1024).decode())
                    print(e)

on the client side, function incoming_message is in its own thread as well. I used select to check if the sockets that I had received from the server is readable.

If it is readable, i would try to unpack the bytes that are sent over.

I tried to use try and except error handling to look for any unpickling error. From what i had understood, if pickle.loads() is unable to load the received bytes, the except block would handle it by printing the sockets bytes as it as.

Any help or advices would be appreciated! Thanks

EDIT:

This is the error:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
internn00b
  • 11
  • 3
  • The major security consideration of loading a pickle from network aside, if the number of bytes of a picked board state is larger than `1024`, it may be possible that `sockets.recv(1024)` result in an incomplete pickled bytes - the byte 0 (first byte) being `0x80` indicates that it might be a pickle. Simply read and buffer the message until a complete pickle is received before `loads`. More details in [thread](https://stackoverflow.com/questions/48974070/object-transmission-in-python-using-pickle). – metatoaster Jan 08 '23 at 10:29
  • As for ensuring the correct decoding of data sent (which may have been done through multiple sends and/or multiple reads) as a singular entity, you may need additional work, see [thread](https://stackoverflow.com/questions/58822272/python-socket-putting-data-of-multiple-send-into-one-receive-buffer) as a start. – metatoaster Jan 08 '23 at 10:38
  • For something simple (like your use case where it's a 2-D list) I imagine using JSON be a lot safer (and easier), just use `json.dumps` and `json.loads` instead for _all_ data, including a single string. – metatoaster Jan 08 '23 at 10:39
  • @metatoaster thanks for the reply! Following your advice, I have changed all pickles to json and yeah, its working great! Thanks! – internn00b Jan 08 '23 at 10:48
  • @metatoaster as for what you had mention, "decoding of data sent" could you help me to visualize what is going on in the backend when i'm using select.select? From the server, if i were to send 2 concurrent data over to client, is there any queue in the background that is added automatically? How is the data even being formatted even before being transferred over the network? Thanks – internn00b Jan 08 '23 at 10:49
  • I was referring to your use of `sockets.recv(1024).decode()` or `pickle.loads(received_board)` - first case is you never verified that it's not binary data, the second case you never verified that everything has been received. The data is ultimately encoded as a sequence of `bytes` and you, as the programmer, will need to come up with (or use) the correct encoding/decoding scheme, which is why I refer you to that thread, where it links to four additional answers - please refer to them. – metatoaster Jan 08 '23 at 22:27

0 Answers0