tutorial real example español python django sockets websocket

real - Hacer movimientos con websockets y python/django(/ twisted?)



django channels tutorial español (4)

La parte divertida de websockets es enviar contenido esencialmente no solicitado desde el servidor al navegador, ¿verdad?

Bueno, estoy usando django-websocket por Gregor Müllegger. Es una oportunidad realmente maravillosa para hacer que los websockets funcionen en Django.

He logrado "hola mundo". La forma en que esto funciona es: cuando una solicitud es un websocket, un objeto, websocket, se agrega al objeto de solicitud. Por lo tanto, puedo, en la vista de interpretar el websocket, hacer algo como:

request.websocket.send(''We are the knights who say ni!'')

Eso funciona bien Recibo el mensaje en el navegador como un amuleto.

Pero, ¿qué sucede si quiero hacerlo sin emitir ninguna solicitud desde el navegador?

OK, entonces primero guardo el websocket en el diccionario de sesión:

request.session[''websocket''] = request.websocket

Luego, en un shell, voy y tomo la sesión por clave de sesión. Efectivamente, hay un objeto websocket en el diccionario de sesión. ¡Contento!

Sin embargo, cuando trato de hacer:

>>> session.get_decoded()[''websocket''].send(''With a herring!'')

Yo obtengo:

Traceback (most recent call last): File "<console>", line 1, in <module> error: [Errno 9] Bad file descriptor

Triste. :-(

OK, entonces no sé mucho sobre sockets, pero sé lo suficiente como para husmear en un depurador, y he aquí, veo que el socket en mi depurador (que está vinculado al websocket genuino de la solicitud) tiene fd = 6, mientras que el que tomé del websocket guardado de sesión tiene fd = -1.

¿Puede una persona orientada a socket ayudarme a resolver estas cosas?


Como señaló Gregor Müllegger, WSGI no puede manejar Websockets correctamente, porque ese protocolo nunca fue diseñado para manejar esa característica. uWSGI , desde la versión 1.9.11, puede manejar Websockets de uWSGI . Aquí uWSGI se comunica con el servidor de aplicaciones utilizando HTTP sin procesar en lugar del protocolo WSGI. Un servidor escrito de esa manera, por lo tanto, puede manejar las partes internas del protocolo y mantener la conexión abierta durante un período prolongado. No es una buena idea que las conexiones de larga duración sean manejadas por una vista de Django, porque entonces bloquearían un hilo de trabajo, que es un recurso limitado.

El objetivo principal de Websockets es hacer que el servidor envíe mensajes al cliente de forma asíncrona. Esta puede ser una vista de Django activada por otros navegadores (por ej .: clientes de chat, juegos de varios jugadores) o un evento activado por, digamos, django-apio (por ej .: resultados deportivos). Por lo tanto, es fundamental para estos servicios de Django, utilizar una cola de mensajes para enviar mensajes al cliente.

Para manejar esto de una manera escalable, escribí django-websocket-redis , un módulo de Django que puede mantener abiertas todas esas conexiones Websocket de larga vida en un solo proceso / subproceso usando Redis como la cola de mensajes de fondo.


Podrías darle una oportunidad a Stargate: http://boothead.github.com/stargate/ y http://pypi.python.org/pypi/stargate/ .

Está construido sobre la pirámide y el evento (también contribuí bastante con el soporte y pruebas de websocket para eventlet). La gran ventaja de la pirámide para este tipo de cosas es que tiene el concepto de un recurso al que se asigna la URL, en lugar de solo el resultado de un reclamo. Así que terminas con un gráfico de recursos persistentes que se correlaciona con tu estructura de url y las conexiones de websocket simplemente se enrutan y se conectan a esos recursos.

Entonces terminas haciendo solo dos cosas:

class YourView(WebSocketView): def handler(self, websocket): self.request.context.add_listener(websocket) while True: msg = websocket.wait() # Do something with message

Para recibir mensajes y

resource.send(some_other_message)

Aquí el recurso es una instancia de un stargate.resource.WebSocketAwareContext (como lo es self.request.context) arriba y el método de envío envía el mensaje a todos los clientes conectados con el método add_listener.

Para publicar un mensaje a todos los clientes conectados, simplemente llame a node.send(message)

Espero poder escribir una pequeña aplicación de ejemplo en la próxima semana o dos para demostrar esto un poco mejor.

Siéntase libre de hacerme ping en github si quiere ayuda con eso.


Soy el autor de django-websocket. No soy un verdadero experto en el tema de websockets y redes, sin embargo, creo que tengo una comprensión decente de lo que está pasando. Perdón por entrar en gran detalle. Incluso si la mayoría de la respuesta no es específica para su pregunta, podría ayudarlo en algún otro momento. :-)

Cómo funcionan los websockets

Déjame explicar en breve qué es un websocket. Un websocket comienza como algo que realmente parece una simple solicitud HTTP, establecida desde el navegador. Indica a través de un encabezado HTTP que desea "actualizar" el protocolo para que sea un websocket en lugar de una solicitud HTTP. Si el servidor admite websockets, acepta el handshake y tanto el servidor como el cliente ahora saben que usarán el zócalo tcp establecido anteriormente utilizado para la solicitud HTTP como una conexión para intercambiar mensajes websocket.

Además de enviar y esperar mensajes, también tienen la capacidad de cerrar la conexión en cualquier momento.

Cómo django-websocket abusa del entorno de solicitud wsgi de Python para secuestrar el socket

Ahora veamos los detalles de cómo django-websocket implementa la "actualización" de la solicitud HTTP en un cylce de solicitud-respuesta django.

Django usualmente usa la especificación WSGI para hablar con el servidor web como apache o gunicorn etc. Esta especificación fue diseñada solo con el modelo de comunicación muy limitado de HTTP en mente. Supone que obtiene una solicitud HTTP (solo datos entrantes) y devuelve la respuesta (solo datos salientes). Esto hace que sea difícil forzar django en el concepto de un websocket donde se permite la comunicación bidireccional.

Lo que estoy haciendo en django-websocket para lograr esto es que profundizo mucho en las partes internas de WSGI y el objeto de solicitud de django para recuperar el socket subyacente. Este socket tcp se usa para manejar la actualización de la solicitud HTTP directamente a una instancia de websocket.

Ahora a tu pregunta original ...

Espero que lo anterior haga obvio que cuando se establece un websocket, no tiene sentido devolver un HttpResponse. Esta es la razón por la que generalmente no devuelve nada en una vista manejada por django-websocket.

Sin embargo, quería mantenerme cerca del concepto de una vista que contiene la lógica y devuelve datos basados ​​en la entrada. Es por eso que solo debe usar el código en su vista para manejar el websocket.

Después de regresar de la vista, el websocket se cierra automáticamente. Esto se hace por una razón: no queremos mantener el socket abierto durante un tiempo indefinido y confiar en que el cliente (el navegador) lo cierre.

Es por esto que no puedes acceder a un websocket con django-websocket fuera de tu vista. El descriptor de archivo se establece, por supuesto, en -1, lo que indica que ya está cerrado.

Renuncia

Ya expliqué que estoy cavando en el entorno circundante de django para obtener de alguna manera, de una manera muy hackosa, acceso al zócalo subyacente. ¡Esto es muy frágil y tampoco debería funcionar, ya que WSGI no está diseñado para esto! También expliqué anteriormente que el websocket se cierra después de que finaliza la vista; sin embargo, después de que el websocket se cerró (Y cerró el socket tcp), la implementación de WSGI de django intenta enviar una respuesta HTTP: no sabe sobre websockets y cree que está en un ciclo de solicitud-respuesta HTTP normal. Pero el socket ya está cerrado y el envío fallará. Esto generalmente causa una excepción en django.

Esto no afectó mis pruebas con el servidor de desarrollo. El navegador nunca se dará cuenta (ya sabes ... el socket ya está cerrado ;-) - pero generar un error no controlado en cada solicitud no es un concepto muy bueno y puede perder memoria, no controla la conexión de la base de datos correctamente y muchas cosas eso se romperá en algún momento si usa django-websocket para más que experimentar.

Es por eso que realmente te aconsejo que no uses websockets con django todavía. No funciona por diseño. Django y especialmente WSGI necesitarían una revisión total para resolver estos problemas (vea esta discusión para websockets y WSGI ). Desde entonces sugeriría usar algo como eventlet . Eventlet tiene una implementación de websocket en funcionamiento (pedí prestado algún código de eventlet para la versión inicial de django-websocket) y dado que es simplemente código python puedes importar tus modelos y todo lo demás desde django. El único inconveniente es que necesita un segundo servidor web que se ejecuta solo para manejar websockets.


request.websocket probablemente se cierre cuando regrese del controlador de solicitud (vista). La solución simple es mantener vivo el controlador (al no regresar de la vista). Si su servidor no es multiproceso, no podrá aceptar ninguna otra solicitud simultánea.