0

in order to send numpy arrays(images) over socket i used pickle, but sometimes it says "_pickle.UnpicklingError: pickle data was truncated" i found some fixes but they didn't work for one reason: i'm sending array every time possible, because im making a screen-share script, and the solutions were to put a loop that takes parts of the msg and combines them. and since i'm sending the array every while loop run it combines all the arrays together. what can i do?

server:

import cv2
import numpy
import pyautogui
import pickle
import time
import socket
a = pickle.dumps(numpy.array([4556]))
print (a)
b = pickle.loads(a)
print(b)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((socket.gethostname(), 3948))
server.listen()
client, addres = server.accept()
print("listening")
while True:
    screen = pyautogui.screenshot()
    cv2thing = numpy.array(screen)
    rgb = cv2thing[...,::-1].copy()
    client.send(pickle.dumps(rgb))
    print("sent")
    if cv2.waitKey(1) == ord('q'):
        break

client:

import socket
import cv2
import pickle

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((socket.gethostname(), 3948))
print("connected")
while True:
    cv2.imshow('frame', pickle.loads(client.recv(999999999)))

    if cv2.waitKey(1) == ord('q'):
        cv2.destroyAllWindows()
        break
2
  • You aren't using sendall(); there's no guarantee send() sends everything. Commented Mar 23, 2021 at 10:44
  • It's highly inefficient sending uncompressed RGB screen grabs over a network... an RGB full-screen grab of an iMac Pro is 43MB, so you'll get 2 frames per second on Gigabit Ethernet. You may want to encode as PNG or JPEG before sending. Commented Mar 23, 2021 at 10:55

1 Answer 1

2

It is a common error with TCP sockets. The TCP protocol is a stream protocol. It guarantees that all sent bytes will be received in the same order, but makes no guarantees at the packet level. That means that the packets may be splitted or recombined by any element on the connection: the sender network drivers, the reciever part or any element on transit (router, gateway, etc.).

To relyably recieve a full message, you must be prepared to re-assemble packets in case the message has been splitted. But that also means that you must use a higher level protocol to mark the end of a message. The simplest one is to shutdown (not close...) the socket after sending the message, but it can only be used for a single message per connection.

But here you could use the fact that pickle can natively use a stream protocol and does not rely on packet sizes, provided you have a file like object. IMHO a simple way is to build a minimal wrapper over a socket. You can build one by hand:

class SocketReader(io.RawIOBase):
    def __init__(self, s):
        self.s = s
    def readable(self):
        return True
    def readinto(self, b):
        return self.s.recv_into(b)

but it is simpler to use the makefile method of Python sockets:

import socket
import cv2
import pickle
import io

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((socket.gethostname(), 3948))
print("connected")
fd = client.makefile()
while True:
    cv2.imshow('frame', pickle.load(fd))

    if cv2.waitKey(1) == ord('q'):
        cv2.destroyAllWindows()
        break
Sign up to request clarification or add additional context in comments.

4 Comments

There's also sock.makefile(), no need to wrap things by hand...
@AKX: Thank you! I knew something like that should exist, but I could not find it...
it didn't work since pickle.load() doesn't get this wrapper object, any method in the class that gets me the bytes msg?
@DeepSpace9: Can you explain what means pickle.load() doesn't get this wrapper object? It worked find in my own tests...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.