I am trying to send an image from a client to the server, using python sockets. The problem is the entire image gets sent only occasionally, and fails most of the times.
Here is a summary of what I’m doing: First I load an image from a file on the client’s computer, convert it to a numpy array and then to a bytes object, which I then send to the server. I add a “done” at the end of the bytes object to let the server know that the image transmission is complete.
However, although the “done” at the end of the bytes object gets sent every time, the entire image itself (which should be sent before the “done”) doesen’t. It works perfectly once in a while but not always.
Here is full reproducible code:
server.py
import numpy as np
from _thread import start_new_thread
IP = "0.0.0.0" # Address to bind to
PORT = 5555 # Arbitrary non-privileged port
DEFAULT_BYTES = 2048
total_connections_so_far = 0
# function to send some data to a client
def send(data, conn):
try:
pickled_data = pickle.dumps(data)
conn.sendall(pickled_data)
except Exception as e:
print(e)
# recieve some data
def recieve_data(conn, size=DEFAULT_BYTES):
data = conn.recv(size)
return data
# deal with sending and recieving data from and to a user
def threaded_client(conn, addr):
# send this user some start up info
send(str.encode("You are now connected to the server!", "utf-8"), conn)
while True:
try:
data = recieve_data(conn)
# user has disconnected
if not data:
break
data = pickle.loads(data) # data comes in as pickle encoded bytes
reply = {} # initiate a reply, to send to the user
# user has updated something
if data.get("image"):
dtype = data["image"]["dtype"]
size = data["image"]["size"]
shape = data["image"]["shape"]
full_image = b""
print("Total bytes supposed to be recieved:", size)
image_bytes = b"" # image will come in as bytes
# the last 4 bytes will be "done" - to inform that the image transmission is complete
while image_bytes[-4:] != b"done":
image_bytes = recieve_data(conn,)
full_image += image_bytes
# print(len(full_image), image_bytes[-4:])
full_image = full_image[
:-4
] # get rid of the last 4 bytes, which are gonna be "done"
print("Total bytes recieved: ", len(full_image))
# convert buffer to numpy array
full_image = np.frombuffer(full_image, dtype=dtype)
# reshape into 2d
full_image = full_image.reshape(*shape)
# surf = pygame.surfarray.make_surface(full_image)
# pygame.image.save(surf, "test.png")
reply["recieved"] = len(
full_image
) # send the number of bytes recieved to the client
send(reply, conn)
except Exception as e:
print(e)
break
# the user has disconnected
print(f"Disconnected: {addr}")
# close the connection
conn.close()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# bind the socket to the host address and port
s.bind((IP, PORT))
print("Server started at: ", s.getsockname())
# listen for connections
s.listen()
print("Server has started. waiting for connections...")
while True:
# accept any connectionn
conn, addr = s.accept()
print("Connected by", addr)
total_connections_so_far += 1 # increment the totoal connections
start_new_thread(threaded_client, (conn, addr,))
client.py
( you may have to send the image to the server a couple of times to encounter the error )
import socket, pickle, pygame, os
class Network:
def __init__(self):
# initialise the client
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = "" # the ip address of the server
self.port = 5555 # port of the server
self.addr = (
self.server,
self.port,
) # complete address, to which we can now connect to
# function to connect to the server
def connect(self):
try:
self.client.connect(self.addr)
print("Connected!")
# the server sends some text in the beginning
data = self.recv()
print(data.decode("utf-8"))
except Exception as e:
print("Could not connect to server!")
print(e)
# send some data to the server
def send(self, data, pickle_data=True):
try:
if pickle_data:
data = pickle.dumps(data)
self.client.sendall(data)
except Exception as e:
print(e)
return None
# recieve some data from the server
def recv(self, buffer_size=2048, load_pickle=True):
try:
data = self.client.recv(buffer_size)
if not data:
return None
return pickle.loads(data) if load_pickle else data
except Exception as e:
print(e)
return None
def main():
n = Network()
n.connect()
base_path = os.path.dirname(__file__)
while True:
filepath = input("Paste in the absolute path to the image file: ")
filepath = os.path.join(base_path, filepath)
try:
image = pygame.image.load(filepath) # load the image using pygame
except Exception as e:
print(e)
continue
# get a numpy array of pixels from the image
pixel_array = pygame.surfarray.array2d(image)
send_image(pixel_array, n)
print(n.recv()) # print out the number of bytes recieved by the server
def send_image(arr, n):
image_bytes = arr.tobytes() # convert it to bytes
image_bytes += b"done"
size = len(image_bytes) # the size of the image + the "done" at the end
print(image_bytes)
# send the server the info about the image
n.send({"image": {"dtype": arr.dtype, "shape": arr.shape, "size": size}})
# send the image bytes
n.send(image_bytes, pickle_data=False)
main()
I want to be able to send 100% of the image all the time, Please help me out!
Thanks!