pythonsocketsclient-server

How to send an image in a Client/Server architecture with Socket python


I'm trying to send an image from a server to a client using sockets in python. But when the client receives the image, I try to display it using Image() and Byteio(), but an error is displayed:

I tried to find out where this error was coming from, so I tried displaying the bit size of the image received on the client side and comparing it with the bit size of the image on the server side, and I realised that there was a difference. The bit size on the server side is greater than that on the client side. Maybe that's where the problem lies. If so, how do you deal with this problem? Or how do you transfer images through a client/server architecture in Python with sockets?

My server code:

import socket
import mysql.connector
from mysql.connector import errorcode
import json
from datetime import datetime
from io import BytesIO
from PIL import Image

host = 'localhost'
port = 50000

mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mySocket.bind((host, port))
mySocket.listen()

while 1:
    connexion, adresse = mySocket.accept()
    with open('WhatsApp Image 2023-10-16 at 17.49.56.jpeg', 'rb') as fichier_image:
        image = fichier_image.read()
    if type(image)==bytes:
        try:
            taile_image = len(image)
            print(taile_image)
            print(taile_image.to_bytes(4, byteorder='big'))
            connexion.sendall(taile_image.to_bytes(4, byteorder='big'))
            connexion.sendall(image)
        except:
            pass
    else:
        try:
            resultat_str = json.dumps(resultat, default=str)
            connexion.send(resultat_str.encode('utf8'))
        except:
            pass
    connexion.close()
    ch = input("<R>ecommencer <T>erminer ? ")
    if ch.upper() == "T":
        break
mySocket.close()

and this is my client code:

import socket, threading
import json
from io import BytesIO
from PIL import Image

host = 'localhost'
port = 50000

class ThreadEmission(threading.Thread):
    def __init__(self, conn):
        threading.Thread.__init__(self)
        self.connexion = conn 

    def run(self):
        ident = 2
        pays='Guinée'
        msgClient = "SELECT logo FROM entreprise WHERE identreprise=%s"%ident
        self.connexion.send(msgClient.encode('utf8'))
        

class ThreadReception(threading.Thread):
    def __init__(self, conn):
        threading.Thread.__init__(self)
        self.connexion = conn 
        self.resultat = None

    def run(self):
        
        #print(msgServeur)
        try:
            msgServeur = self.connexion.recv(1024).decode('utf8')
            self.resultat = json.loads(msgServeur)
        except :
            taille_image_bytes = self.connexion.recv(4)
            print('taille_image_bytes ',taille_image_bytes)
            taille_image = int.from_bytes(taille_image_bytes, byteorder='big')
            print('taille_image', taille_image)
            self.resultat= self.connexion.recv(taille_image)
            print(len(self.resultat))       
            #print (type(msgServeur))

        self.connexion.close()
        
    def get_resultat(self):
        return self.resultat
            

myClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
myClient.connect((host, port))

th_E = ThreadEmission(myClient)
th_R = ThreadReception(myClient)    
#th_E.start()
th_R.start()
#th_E.join()
th_R.join()
resultat = th_R.get_resultat()
print(resultat)

print(type(resultat))
image = Image.open(BytesIO(resultat))
image.show()

Solution

  • Here is a minimal example with the server sending one image on client connection and the client receiving the image. As mentioned in the comments, the OP code had unrelated code to handle a possible JSON response that is outside the scope of the question and was interfering with reception of the image.

    Note that recv(n) receives 1-n bytes or 0 on disconnect. You are responsible for buffering data until you receive a complete message. In this case make sure you read exactly four bytes for the length, then exactly "length" bytes for the data. Using socket.makefile can help with buffering by wrapping the socket in a file-like object where .read(n) is available to read exactly n bytes unless an error or EOF (socket close) is encountered.

    Server:

    import socket
    
    with socket.socket() as sock:
        sock.bind(('', 50000))
        sock.listen()
    
        while True:
            client, addr = sock.accept()
            print(f'{addr}: connected')
            with open('some_image.jpg', 'rb') as file:
                image = file.read()
            with client:
                length = len(image)
                client.sendall(length.to_bytes(4, byteorder='big'))
                client.sendall(image)
            print(f'{addr}: disconnected')
    

    Client:

    import socket
    import threading
    import io
    from PIL import Image
    
    with socket.socket() as sock:
        sock.connect(('localhost', 50000))
        with sock.makefile('rb') as infile:
            header = infile.read(4)
            if len(header) == 4:
                length = int.from_bytes(header, byteorder='big')
                image = infile.read(length)
                if len(image) == length:
                    image = Image.open(io.BytesIO(image))
                    image.show()
                else:
                    print('incomplete image')
            else:
                print('incomplete header')