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.
elbis says:
excelente bro, muchas gracias me funciono genial