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.
Gaael says:
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.
Hector Trejo says:
fmt = «<Q" una pregunta que hace esta funcion y que significa fmt
Recursos Python says:
Hola.
fmt
viene de format, que es el formato que se emplea para codificar la información en binario.<Q
indica que el dato codificado es de 8 bytes (un número suficientemente grande para codificar el tamaño del archivo). Es un tema complejo, pero acá tenés la referencia oficial. Te dejo además una explicación adicional que hice para otro usuario del foro sobre el mismo tema: https://foro.recursospython.com/Thread-Intentado-comprender-este-codigo-Enviar-archivo-via-socket?pid=3221#pid3221.Saludos
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.
Recursos Python says:
Hola, Brandon. Si tenés dudas podés crear un tema en el foro para que lo veamos con más detalle allí.
Saludos
brandon santibañez says:
aqui amigo. porfa. https://foro.recursospython.com/Thread-Intentado-comprender-este-codigo
Gracias y saludos..
elbis says:
excelente bro, muchas gracias me funciono genial
Recursos Python says:
¡De nada!