La semana pasada os traje un tutorial sobre cómo crear de forma rápida y muy sencilla nuestra propia aplicación cliente – servidor en Python. He seguido trabajando en ella añadiendo un par de funcionalidades curiosas que pueden hacer divertido el ejercicio. En este caso lo he convertido en una especie de System Killer. Tiene dos funcionalidades: quemar cpu y quemar ram. Con ellas ponemos en marcha una serie de bucles dando la orden desde el cliente que permiten poner a funcionar la CPU y llenar toda la RAM pudiendo provocar la caída del sistema. ¿Útil? Creo que es poco útil. Ahora, divertido es un rato.Podeis obtener el código de la primera versión del proyecto aquí. Lo único que hace este código es recibir mensajes del cliente y responder a ellos. Si mandas un “exit” cierras la conexión.

Vamos a trabajar sobre este proyecto y, al final, tendréis también el código fuente de la versión 2 terminada.

runEver

Este es el nombre que he pensado para la primera función. Consiste en una secuencia infinita de mensajes entre el cliente y el servidor.

El código base es el siguiente:

if msg == "runEver":
        while True:
            cliente.send("running")
            respuesta = cliente.recv(4096)
            print respuesta

Debemos introducirlo dentro del bucle de funcionamiento del cliente. De tal forma que quedaría así:

while True:
    msg = raw_input("> ")
    if msg == "runEver":
        while True:
            cliente.send("running")
            respuesta = cliente.recv(4096)
            print respuesta
    cliente.send(msg)
    respuesta = cliente.recv(4096)
    print respuesta
    if respuesta == "exit":
        break;

Pero en el servidor también tenemos cosas que modificar para que todo funcione correctamente. Cuando el servidor reciba el mensaje “runEver” debe decirle al cliente “me too”. De esta forma se mantendrá el bucle infinitamente.

if datos == "running":
        print("Recibido: %s" %datos)
        cliente.sendall("me too")
        continue

Ejecutamos cliente y servidor en distintas terminales. Enviamos el mensaje “runEver” desde el cliente y observamos los resultados:

Y observamos que el uso de cpu se ha disparado:

ramKiller

Ahora tiene algo más de complegidad. Lo que haremos será enviar datos al servidor de manera infinita y que éste los vaya almacenando en una estructura en memoria de forma que comience a ocupar espacio en memoria ram. Cuidado: si lo ejecutas hasta llenar la ram tu sistema seguramente se quede totalmente pillado y debas forzar el apagado.

Cambios en el servidor

Primero debemos crear un comando que podamos enviar desde el cliente y active el modo ramKiller en el servidor. Este comando será “ramKiller”. Debemos habilitar el servidor para que, cuando reciba “ramKiller” se active el modo deseado:

if datos == "ramKiller":
        print("Recibido: %s" %datos)
        print("Modo ramKiller activadoo")
        cliente.send("RAMKILLER01")
        print("Esperando mensaje de inicio")
        datos = cliente.recv(1024)
        if datos == "start":
            print("Ahora empezariamos el bucle")
        continue

Ahora crearemos en el servidor la función a la que llamaremos en vez de imprimir “Ahora empezariamos el bucle”:

def ramKill():
    print("RAMKILL INICIADO. PELIGRO. EL SISTEMA PUEDE CAERSE")
    estructura = []
    contador = 0
    while True:
        estructura.append("Insertado %s" %contador)
        cliente.send("Insertado %s\n" %contador)
        contador += 1
    return

Y cambiamos la línea donde antes ponia “print(“ahora empezariamos el bucle”)” por la llamada a ramKill().

Cambios en el cliente

Si nos fijamos en el nuevo código del servidor vemos que cuando recibimos el “start” que confirma el inicio del ramKiller enviamos al cliente un mensaje que contiene “RAMKILLER01”. Cuando el cliente reciba esto será la confirmación de que se ha abierto el proceso ramKill() en el servidor y, por tanto, el debe ponerse constantemente a la escucha para monitorizar la cantidad de datos añadidos a la estructura en tiempo real. Por tanto, en el cliente también creamos un método ramKill():

def ramKill():
    while True:
        data = cliente.recv(1024)
        print(data)

Y a este método lo llamamos cuando recibimos el mensaje de confirmación adecuado:

if respuesta == "RAMKILLER01":
        print("Inicie secuencia")
        msg = raw_input("> ")
        if  msg == "start":
            cliente.send(msg)
            ramKill()

Ejecución

Tras toquetear el código volvemos a revisarlo y pulimos algunos detalles para que en caso de iniciar el ramKiller pero no mandar el “start” el programa vuelva a su modo original. También mejoro una cosita: hasta ahora el programa suponía que se iban a mandar mensajes con contenido. Si no escribimos nada en el cliente y lo enviamos el programa comenzará a funcionar mal. También he arreglado eso.

Ejecutamos el servidor y el cliente tal y como veíamos en el capítulo de la semana pasada y comenzamos a mandar mensajes desde el cliente para ver que todo funciona: el servidor muestra el mensaje recibido y manda una cláusula de recibido al cliente. Ahora ejecutamos el ramKiller y el cliente nos indica que indiquemos la secuencia de inicio. Escribimos start y lo mandamos. Vemos como el servidor imprime el mensaje de aviso y le manda al cliente las inserciones que está realizando en tiempo real:

Y si monitorizamos el uso del hardware vemos que el procesador ha empezado a ponerse a pensar y que el uso de la ram aumenta linealmente:

Y si cerramos el cliente (ctrl + c) provocando la caída tanto del cliente como del servidor podemos ver como todo vuelve a la normalidad:

Si no cerramos el proceso el servidor continuará hasta llenar la ram y se quedará congelado. Deberemos forzar reinicio por hardware.

El código completo de ambos archivos (cliente y servidor) podéis encotrarlos aquí en GitHub. ¡Espero que os divirtáis tanto como yo!