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.

Curso online 👨‍💻

¡Ya lanzamos el curso oficial de Recursos Python en Udemy! Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter en 2024.

Consultoría 💡

Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.

9 comentarios.

  1. me sale este error saben por que

    File «c:\Users\GAEL\Desktop\kkkkk\Cli.py», line 15, in
    with socket.create_connection((«localhost», 6190)) as conn:
    File «C:\Users\GAEL\AppData\Local\Programs\Python\Python310\lib\socket.py», line 845, in create_connection
    raise err
    File «C:\Users\GAEL\AppData\Local\Programs\Python\Python310\lib\socket.py», line 833, in create_connection
    sock.connect(sa)
    ConnectionRefusedError: [WinError 10061] No se puede establecer una conexión ya que el equipo de destino denegó expresamente dicha conexió

    • Recursos Python says:

      Hola, ¿ejecutaste el servidor antes que el cliente? Y probá también deshabilitando el firewall de Windows o agregando una excepción para el proceso de Python.

  2. 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