Tutorial envio de archivos usando sockets

Iniciado por Once, Noviembre 22, 2013, 01:46:12 AM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Hola, sé que en internet se encuentran multitud de ejemplos acerca de
cómo hacer lo que voy a explicar a continuación pero como no es la primer
vez que me lo preguntan, decidí escribir este pequeño tutorial y
explicarlo lo más sencillo que pueda:

¿Qué usaremos?

  • Python 2.7

Problemas con los sockets:

En python es muy fácil escribir aplicaciones cliente/servidor, sólo
tenemos que echar mano de la libreria nativa No tienes permitido ver los links. Registrarse o Entrar a mi cuentade Python.

Pero una vez comenzamos a trabajar con ésta libreria, nos damos cuenta
que se nos presentan un par de problemas o incovenientes que a veces no
hallamos la manera de solucionar, entre ellos se encuentra que el método
del objeto socket sólo nos permite recibir hasta una cantidad
FIJA de bytes.

Sí se envian datos menores o iguales a la cantidad establecida en nuestro
método recv recibiremos el mensaje sin problemas. Pero ¿y si el mensaje
es mayor a la cantidad de bytes establecidos?

La solución:

La solución es relativamente sencilla, pero suele ser un poco confusa así
que intentaré explicarlo de la forma más sencilla.

Programaremos un protocolo para nuestro cliente servidor.

Muy bonito todo, pero, ¿Qué es un protocolo?

Definamos protocolo como un simple conjunto de reglas que usan dos máquinas
para comunicarse. Por ejemplo: si dos personas (un bartener y un cliente)
hablan idiomas distintos (inglés y español) jamás se entenderán y el cliente
jamás obtendra su cerveza; pero si hablan el mismo idioma (inglés-inglés o español-español)
se entenderán y el cliente tendrá su cerveza y el bartender su dinero.

En este ejemplo, el idioma -ya sea español o inglés- es el protocolo que
las dos máquinas (el cliente y el bartender) usan para comunicarse, pero tienen
que usar el mismo protocolo -hablar el mismo idioma-.

Sí hasta ahora has entendido, nuestro siguiente paso será diseñar nuestro
protocolo

Ahora sí, manos a la obra:

Antes definamos el trabajo de cada aplicación:

Cliente: Se encargará de enviar el archivo
Servidor: Se encargará de recibir y guardar el archivo en el disco duro

Teniendo en cuenta esto, ya nos damos una idea de lo que queremos hacer:
el cliente enviará la longitud (cantidad de bytes) del archivo que quiere enviar, el servidor
recibe la longitud y se prepara para recibir el archivo; avisa al cliente
que está listo para recibir el archvio. El cliente envia el archivo.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

En la imagen de arriba (disculpen el paint :P), trato de explicar un poco mejor
cómo funcionará nuestro protocolo.

Las líneas rojas representan los datos que son enviados por el cliente hacía el servidor
Las líneas amarillas representan los datos que son enviados por el servidor hacía el cliente
Los bloques negros -los que parecen una T- significan que los datos que son recibidos
deben ser verificados para continuar.

Básicamente quedaría así:

Cliente: Enviar longitud archivo.
Servidor: Comprobar la longitud (debe ser un número).
Servidor: En caso que la longitud sea correcta, avisar al cliente para que envie al archivo.

Cliente: Comprobar que el servidor está listo.
Cliente: Enviar archivo al servidor.

Código del cliente:

Código: python
# -*- coding: utf-8 -*-

# Envio de archivos: cliente
# 11Sep

import socket

# Creamos una lista con la dirección de
# la máquina y el puerto donde
# estara a la escucha
CONEXION = (socket.gethostname(), 9001)
ARCHIVO = "a.pdf"


# Instanciamos el socket y nos
# conectamos
cliente = socket.socket()
cliente.connect(CONEXION)

# Abrimos el archivo en modo lectura binaria
# y leemos su contenido
with open(ARCHIVO, "rb") as archivo:
    buffer = archivo.read()

while True:
    # Enviamos al servidor la cantidad de bytes
    # del archivo que queremos enviar
    print "Enviando buffer"
    cliente.send(str(len(buffer)))
   
    # Esperamos la respuesta del servidor
    recibido = cliente.recv(10)
    if recibido == "OK":
        # En el caso que la respuesta sea la correcta
        # enviamos el archivo byte por byte
        # y salimos del while
        for byte in buffer:
            cliente.send(byte)
        break


Código del servidor:

Código: python
# -*- coding: utf-8 -*-

# Envio archivos: servidor
# 11Sep

import socket

# Creamos una lista con los datos del la conexión
CONEXION = (socket.gethostname(), 9001)

servidor = socket.socket()

# Ponemos el servidor a la escucha
servidor.bind(CONEXION)
servidor.listen(5)
print "Escuchando {0} en {1}".format(*CONEXION)
# Aceptamos conexiones
sck, addr = servidor.accept()
print "Conectado a: {0}:{1}".format(*addr)
while True:
    # Recibimos la longitud que envia el cliente
    recibido = sck.recv(1024).strip()
    if recibido:
        print "Recibido:", recibido
    # Verificamos que lo que recibimos sea un número
    # en caso que así sea, enviamos el mensaje "OK"
    # al cliente indicandole que estamos listos
    # para recibir el archivo
    if recibido.isdigit():
        sck.send("OK")
       
        # Inicializamos el contador que
        # guardara la cantidad de bytes recibidos
        buffer = 0
        # Abrimos el archivo en modo escritura binaria
        with open("archivo", "wb") as archivo:
            # Nos preparamos para recibir el archivo
            # con la longitud específica
            while (buffer <= int(recibido)):
                data = sck.recv(1)
                if not len(data):
                    # Si no recibimos datos
                    # salimos del bucle
                    break
                # Escribimos cada byte en el archivo
                # y aumentamos en uno el buffer
                archivo.write(data)
                buffer += 1
           
            if buffer == int(recibido):
                print "Archivo descargado con éxito"
            else:
                print "Ocurrió un error/Archivo incompleto"
        break


Como pueden ver, el código no es nada del otro mundo. Eso sí,
nuestro protocolo tiene un par de fallos potenciales que queda en sus
manos encontrar y solucionar.

Saludos!
11Sep.







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Que excelente aporte 11sep!
Muchisimas gracias bro!

Saludos!


Gracias bro, si tengo tiempo haré un par más para resolver esas dudas tan repetitivas que me hacen por privado.

Saludos!







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Muy bueno el aporte 11sep!
Ya se me ocurrieron algunas formas de aplicarlo.  :D

Saludos!




Hola disculapame estoy itentando enviar esto datos = [1,2,3,4....8000] solo es una prueba pues nesesito hacer lo mismo con unos datos que vienen de un sensor, mas o menos 8mil datos por segundo, mi preugunta es, como lo hago puesto que cuando intento enviar la prueba que mostre anteriormente, me sale el siguiente error "sendall() argument 1 must be string or buffer, not list", y la verdad no se que hacer, espero puedas ayudarme

Hola jcamiloce10, mira que el mismo error te lo está diciendo, no puedes enviar una lista, puedes enviar un string, lo que implica que para enviar la lista debes hacerlo elemento por elemento.

Ahora, mencionas un sensor, explícame mejor que es lo que estás intentando hacer (o crea un post nuevo) así te puedo ayudar mejor, puesto que si vas a usar un sensor, lo que vas a necesitar es, probablemente, usar el protocolo serial. Así que, por favor, danos más información.

Saludos.







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Hola Once, bueno la explicación es la siguiente. Estoy realizando un trabajo con el sensor ADS1298 para electrocardiografia, este sensor lo tengo conectado a una raspberry pi zero w, por medio de protocolo SPI (es el único protocolo que admite el sensor). El sensor ya me entrega convertidos a digital los datos en tramas de aproximadamente 216 bits cada una, es decir me da una "lista" de 27 bytes o 27 posiciones. Mi objetivo es enviar vía WIFI esos datos, es decir 8mil tramas cada segundo, que me entrega el sensor desde la raspberry pi zero w hacia un computador que funja como servidor o receptor, para que este los almacene y los procese de manera rápida y adecuada. Para eso he buscado varios protocolos de transmisión inalámbrica como el ssh (protocolo que es muy pesado por su necesidad de cifrar cada dato enviado a 128 bits), el protocolo MQTT, pero no he encontrado mucha información ni como programarlo en python, y luego están los sockets, que pienso puede ser una buena opcion para mi problema, esa es a grandes rasgos la explicación, espero no haberme extendido mucho, espero que puedas ayudarme, si tienes una sugerencia por esta temática, o conoces un protocolo de comunicación wifi que me permita hacerlo te lo agradecería, y por cierto muchas gracias por tu atención!

En ese caso, como te ddije al principio, lo mejor es que envies los datos de la lista uno por uno, puesto que no puedes enviar la lista como tal, otra opción seria que serialices la lista antes de enviarla, así seria como si enviaras un archivo de texto, y luego, cuando la recibes, deserializas. Para serializar simplemente usas el módulo No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Saludos!







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Once, te agradezco tu ayuda!, funciono perfectamente la serializacion, en serio haz sido de mucha ayuda!

Un placer poderte ayudar, cualquier otra duda que tengas, no dudes en preguntar.

Saludos!







No tienes permitido ver los links. Registrarse o Entrar a mi cuenta