1

I'm trying to send a 2D array across a socket, it needs to be made out of multiple socket.recv() otherwise I will get a _pickle.UnpicklingError: pickle data was truncated error.

I've tried to do this with a while loop that receives packets and appends them to a list until all data is received:

def receive(self):
    packets = []
    while True:
        packet = self.socket.recv(1024)
        if not packet:
            break
        packets.append(packet)

    data = b"".join(packets)
    data = pickle.loads(data)
    return data

Just doing:

def receive(self):
    data = self.socket.recv(1024)
    data = pickle.loads(data)
    return data

Works if I send something smaller than the 2D array e.g. a coordinate pair tuple (2, 3). But not with the 2D array.

I get a OSError: [WinError 10022] when attempting the while loop approach. I have seen that a OSError: [WinError 10022] could be an issue with not binding the socket but I think I have done that.
I can't figure out where connections are closing and am very confused.

Rest of the code:

Server:

import socket
from _thread import start_new_thread

clients = []


def threaded(client):
    while True:
        data = client.recv(1024)
        if not data:
            print('Not Data.')
            break

        # Send the data received from one client to all the other clients.
        for c in clients:
            if c != client:
                c.send(data)

    client.close()


def Main():
    host = ""
    port = 5555
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host, port))
    print("Socket binded to port:", port)

    s.listen(2)
    print("Socket is listening.")

    while True:
        c, addr = s.accept()
        clients.append(c)

        print(f"Connected to: {addr[0]}:{addr[1]}")

        start_new_thread(threaded, (c,))

    s.close()

Client:

import socket
import pickle


class Client:
    def __init__(self):
        self.host = 'localhost'
        self.port = 5555
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.connect((self.host, self.port))

    def send(self, data):
        message = pickle.dumps(data)
        try:
            self.socket.send(message)
        except socket.error as e:
            return str(e)

    def receive(self):
        packets = []
        while True:
            packet = self.socket.recv(1024)
            if not packet:
                break
            packets.append(packet)

        data = b"".join(packets)
        data = pickle.loads(data)
        return data

The client-server I want to make will have 2 clients and each client will send data to the other constantly. How can I correct my code and properly implement this?

wet-dog
  • 21
  • 4

1 Answers1

1

After doing more research and figuring out what was going wrong I got a solution.

The code never got out of this while loop - because data was constantly being sent and some packet was always incoming.

def receive(self):
    packets = []
    while True:
        packet = self.socket.recv(1024)
        if not packet:
            break
        packets.append(packet)

A solution I found was to send a message indicating how big the message being received should be, so I can tell when all the bytes for a particular message have arrived. Links: Sockets Python 3.5: Socket server hangs forever on file receive, Python Socket Receive Large Amount of Data

def send(self, data):
    message = pickle.dumps(data)
    msg_len = len(message)
    try:
        # Send what the total length of the message to be sent is in bytes.
        self.socket.send(msg_len.to_bytes(4, 'big'))
        self.socket.sendall(message)
    except socket.error as e:
        return str(e)

def receive(self):
    remaining = int.from_bytes(self.socket.recv(4), 'big')
    chunks = []
    while remaining:
        # until there are bytes left...
        # fetch remaining bytes or 4096 (whatever smaller)
        chunk = self.socket.recv(min(remaining, 4096))
        remaining -= len(chunk)
        # write to file
        chunks.append(chunk)

    chunks = b"".join(chunks)
    data = pickle.loads(chunks)
    return data
wet-dog
  • 21
  • 4