Enviar archivo vía socket en Python 3

Enviar archivo vía socket en Python 3

El siguiente código establece una conexión cliente-servidor entre dos programas de Python vía el módulo estándar socket y envía un archivo del cliente al servidor. La lógica de la transferencia de archivos está contenida en dos funciones: el cliente define una función send_file() para enviar un archivo a través de un socket e, inversamente, el servidor define receive_file() para recibirlo. De esta manera se pueden mover fácilmente las funciones desde este artículo a otros programas que ya implementen una conexión cliente-servidor. El código está preparado para enviar cualquier formato de archivo y de todos los tamaños. Requiere Python 3.8 o superior (para versiones anteriores, véase un artículo anterior: Enviar archivo vía socket).

Puedes descargar los dos archivos de Python y un archivo de prueba para transferir desde aquí: enviar-archivo-via-socket-python-3.zip.

Servidor:

# server.py

import socket
import struct


def receive_file_size(sck: socket.socket):
    # Esta función se asegura de que se reciban los bytes
    # que indican el tamaño del archivo que será enviado,
    # que es codificado por el cliente vía struct.pack(),
    # función la cual genera una secuencia de bytes que
    # representan el tamaño del archivo.
    fmt = "<Q"
    expected_bytes = struct.calcsize(fmt)
    received_bytes = 0
    stream = bytes()
    while received_bytes < expected_bytes:
        chunk = sck.recv(expected_bytes - received_bytes)
        stream += chunk
        received_bytes += len(chunk)
    filesize = struct.unpack(fmt, stream)[0]
    return filesize


def receive_file(sck: socket.socket, filename):
    # Leer primero del socket la cantidad de 
    # bytes que se recibirán del archivo.
    filesize = receive_file_size(sck)
    # Abrir un nuevo archivo en donde guardar
    # los datos recibidos.
    with open(filename, "wb") as f:
        received_bytes = 0
        # Recibir los datos del archivo en bloques de
        # 1024 bytes hasta llegar a la cantidad de
        # bytes total informada por el cliente.
        while received_bytes < filesize:
            chunk = sck.recv(1024)
            if chunk:
                f.write(chunk)
                received_bytes += len(chunk)


with socket.create_server(("localhost", 6190)) as server:
    print("Esperando al cliente...")
    conn, address = server.accept()
    print(f"{address[0]}:{address[1]} conectado.")
    print("Recibiendo archivo...")
    receive_file(conn, "imagen-recibida.png")
    print("Archivo recibido.")


print("Conexión cerrada.")

Cliente:

# client.py

import os
import socket
import struct


def send_file(sck: socket.socket, filename):
    # Obtener el tamaño del archivo a enviar.
    filesize = os.path.getsize(filename)
    # Informar primero al servidor la cantidad
    # de bytes que serán enviados.
    sck.sendall(struct.pack("<Q", filesize))
    # Enviar el archivo en bloques de 1024 bytes.
    with open(filename, "rb") as f:
        while read_bytes := f.read(1024):
            sck.sendall(read_bytes)


with socket.create_connection(("localhost", 6190)) as conn:
    print("Conectado al servidor.")
    print("Enviando archivo...")
    send_file(conn, "imagen.png")
    print("Enviado.")

print("Conexión cerrada.")

Para probar el código, debes asegurarte de modificar las llamadas a send_file() y receive_file() con la ruta del archivo que quieres enviar y la ruta del archivo donde lo quieres recibir, que en el código actual es un archivo llamado imagen.png que se encuentra en la misma carpeta que los dos archivos de Python, y se recibe como imagen-recibida.png. Hecho esto, ejecútese primero el servidor:

python server.py

Luego, en otra terminal, el cliente:

python client.py

La salida del servidor se verá así:

Esperando al cliente...
127.0.0.1:60331 conectado.
Recibiendo archivo...
Archivo recibido.
Conexión cerrada.

Y la del cliente:

Conectado al servidor.
Enviando archivo...
Enviado.
Conexión cerrada.



1 comentario.

Deja una respuesta