pythonsocketszipclient-server

How to send zip file using Python sockets?


This is my server:

//server.py
import sys                                           
import socket                                                                                             
HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
SIZE = 1024                                          
FORMAT = "utf-8"                                     
s = socket.socket()                                  
s.setsockopt(socket.SOL_SOCKET, 
socket.SO_REUSEADDR, 1)                                                   
s.bind((HOST, PORT))                                                                                      
s.listen(1)                                          
client = s.accept()
while True:                                           
 print(f"\033[33m[*] Listening as {HOST}:{PORT}\033[m")                                                    
 print(f"\033[32m[!] Client connected {client[1]}\033[m")
 client[0].send('copy trash'.encode())                
 filename = client[0].recv(SIZE).decode(FORMAT)
 print(f"[RECV] Receiving the filename.")
 file = open(filename, "w")
 client[0].send("Filename received.".encode(FORMAT))
 data = client[0].recv(SIZE).decode(FORMAT)
 print(f"[RECV] Receiving the file data.")
 file.write(data)
 client[0].send("File data received".encode(FORMAT))
s.close()

This is my client:

//client.py
import sys                                           
import socket                                        
import subprocess                                    
import tqdm
import shutil
                                                 
HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
FORMAT = "utf-8"                                     
SIZE = 1024                                          
s = socket.socket()                                  
s.connect((HOST, PORT))                              
msg = s.recv(1024).decode()                          
print('[*] server:', msg)                            
if(msg.lower() == "copy trash"):                         
 shutil.make_archive("/root/trashcopy", 'zip', "/root/.local/share/Trash")                                 
 file = open("/root/trashcopy.zip", "r")     
 data = file.read() 
 s.send("trashcopy.zip".encode(FORMAT))           
 msg = s.recv(SIZE).decode(FORMAT)                    
 print(f"[SERVER]: {msg}")                            
 s.send(data.encode(FORMAT))                          
 msg = s.recv(SIZE).decode(FORMAT)
 print(f"[SERVER]: {msg}")

What am I trying to do?

I'm trying to make a zip file with files from the recycle bin and send it to the server, however, there's a problem with encoding and an error is thrown at this line:

s.send(data.encode(FORMAT))   

This is the error:

 UnicodeDecodeError: 'utf-8' codec can't decode byte 0x95 in position 124: invalid start byte

How can I fix this? When it's a .txt file, for example, I can send the file without problems.

It's a decoding problem, I've tried to decode in other formats besides utf-8 but it didn't work.


Solution

  • There were several issues with your code. For example, when reading the zip (binary) file, you should read it as bytes, not as string, and send it as bytes. Also, your server needs to know in advance the expected file size to be able to read the file in chunks. You can do that using byte ordering. Below is a working example. Credits go to this for the data receiving approach, which was slightly modified to read the data in chunks, as your code requires.

    EDIT: The server has been modified to support reconnection from the client. Thus, client = s.accept() has been moved to inside the while loop and client's connection gets closed after the file has been received.

    server.py

    import sys                                           
    import socket
    
    HOST = sys.argv[1] if len(sys.argv) > 1 else '0.0.0.0'                                                    
    PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
    SIZE = 1024                                          
    BYTEORDER_LENGTH = 8
    FORMAT = "utf-8"
    s = socket.socket()                                  
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)                                                   
    s.bind((HOST, PORT))                                                                                      
    s.listen(1)                                          
    
    while True:
        client = s.accept()                                          
        print(f"\033[33m[*] Listening as {HOST}:{PORT}\033[m")                                                    
        print(f"\033[32m[!] Client connected {client[1]}\033[m")
    
        print(f"Sending 'copy trash' msg")
        client[0].send('copy trash'.encode())
    
        print(f"[RECV] Receiving the file size")
        file_size_in_bytes = client[0].recv(BYTEORDER_LENGTH)
        file_size= int.from_bytes(file_size_in_bytes, 'big')
        print("File size received:", file_size, " bytes")
        client[0].send("File size received.".encode(FORMAT))
    
        print(f"[RECV] Receiving the filename.")
        filename = client[0].recv(SIZE).decode(FORMAT)
        print(f"[RECV]Filename received:", filename)
        client[0].send("Filename received.".encode(FORMAT))
    
        print(f"[RECV] Receiving the file data.")
        # Until we've received the expected amount of data, keep receiving
        packet = b""  # Use bytes, not str, to accumulate
        while len(packet) < file_size:
            if(file_size - len(packet)) > SIZE:  # if remaining bytes are more than the defined chunk size
                buffer = client[0].recv(SIZE)  # read SIZE bytes
            else:
                buffer = client[0].recv(file_size - len(packet))  # read remaining number of bytes
    
            if not buffer:
                raise Exception("Incomplete file received")
            packet += buffer
        with open(filename, 'wb') as f:
            f.write(packet)
            
        print(f"[RECV] File data received.")
        client[0].send("File data received".encode(FORMAT))
        client[0].close()
    s.close()
    

    client.py

    import sys                                           
    import socket                                        
    import subprocess                                    
    import shutil
    import os
    
    HOST = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1'                                                    
    PORT = int(sys.argv[2] if len(sys.argv) > 2 else 5555)                                                    
    FORMAT = "utf-8"                                     
    SIZE = 1024
    BYTEORDER_LENGTH = 8                                         
    s = socket.socket()                                  
    s.connect((HOST, PORT))  
                                
    msg = s.recv(SIZE).decode()                          
    print('[*] server:', msg)     
    
    if(msg.lower() == "copy trash"):                         
        #shutil.make_archive("/root/trashcopy", 'zip', "/root/.local/share/Trash")                                 
        shutil.make_archive("_trashcopy", 'zip', "Trash")
        
        file_size = os.path.getsize('_trashcopy.zip')
        print("File Size is :", file_size, "bytes")
        file_size_in_bytes = file_size.to_bytes(BYTEORDER_LENGTH, 'big')
        
        print("Sending the file size")
        s.send(file_size_in_bytes)
        msg = s.recv(SIZE).decode(FORMAT)                    
        print(f"[SERVER]: {msg}")
        
        print("Sending the file name")
        s.send("trashcopy.zip".encode(FORMAT))           
        msg = s.recv(SIZE).decode(FORMAT)                    
        print(f"[SERVER]: {msg}")  
        
        print("Sending the file data")    
        with open ('_trashcopy.zip','rb') as f1:
            s.send(f1.read())  
        msg = s.recv(SIZE).decode(FORMAT)
        print(f"[SERVER]: {msg}")
    s.close()