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.



7 comentarios.

  1. brandon santibañez says:

    Que tal amgos. Estoy aprendiendo python. llevo un tiempo programando un programa con socket, justamente estoy buscando como enviar archivos a traves de socket. me cuesta un poco entender el codigo de arriba. pero bueno, creo no puedo pedir que se explique su funcionamiento por cada palabra que escribas. tratare de estudiarlo. Saludos.

Deja una respuesta