Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making moves w/ websockets and python / django ( / twisted? )

Tags:

The fun part of websockets is sending essentially unsolicited content from the server to the browser right?

Well, I'm using django-websocket by Gregor Müllegger. It's a really wonderful early crack at making websockets work in Django.

I have accomplished "hello world." The way this works is: when a request is a websocket, an object, websocket, is appended to the request object. Thus, I can, in the view interpreting the websocket, do something like:

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

That works fine. I get the message back in the browser like a charm.

But what if I want to do that without issuing a request from the browser at all?

OK, so first I save the websocket in the session dictionary:

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

Then, in a shell, I go and grab the session by session key. Sure enough, there's a websocket object in the session dictionary. Happy!

However, when I try to do:

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

I get:

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

Sad. :-(

OK, so I don't know much of anything about sockets, but I know enough to sniff around in a debugger, and lo and behold, I see that the socket in my debugger (which is tied to the genuine websocket from the request) has fd=6, while the one that I grabbed from the session-saved websocket has fd=-1.

Can a socket-oriented person help me sort this stuff out?

like image 740
jMyles Avatar asked Dec 06 '10 06:12

jMyles


People also ask

Is Django good for WebSockets?

Django Channels facilitates support of WebSockets in Django in a manner similar to traditional HTTP views. It wraps Django's native asynchronous view support, allowing Django projects to handle not only HTTP, but also protocols that require long-running connections, such as WebSockets, MQTT, chatbots, etc.

What is the difference between sockets and WebSockets?

WebSocket is a protocol, Socket is an endpoint - got it thanks. Also, a simple google search returned "The current API specification allowing web applications to use this protocol(WebSocket) is known as WebSockets" (note the plural).

How socket is implemented in Django?

Django with SocketIOCreate a socket app in Django project and add it to INSTALLED_APPS . Now inside socketio_app , edit views.py and add following code. Download views.py file from here. This code will create socket events for your applications.

What is WebSocket channel?

A channel is a way to receive continuous updates about a stream or stream set. Rather than using a typical HTTP request, channels are accessed using the Web Socket protocol.


1 Answers

I'm the author of django-websocket. I'm not a real expert in the topic of websockets and networking, however I think I have a decent understanding of whats going on. Sorry for going into great detail. Even if most of the answer isn't specific to your question it might help you at some other point. :-)


How websockets work

Let me explain shortly what a websocket is. A websocket starts as something that really looks like a plain HTTP request, established from the browser. It indicates through a HTTP header that it wants to "upgrade" the protocol to be a websocket instead of a HTTP request. If the server supports websockets, it agrees on the handshake and both - server and client - now know that they will use the established tcp socket formerly used for the HTTP request as a connection to interchange websocket messages.

Beside sending and waiting for messages, they have also of course the ability to close the connection at any time.

How django-websocket abuses the python's wsgi request environment to hijack the socket

Now lets get into the details of how django-websocket implements the "upgrading" of the HTTP request in a django request-response cylce.

Django usually uses the WSGI specification to talk to the webserver like apache or gunicorn etc. This specification was designed just with the very limited communication model of HTTP in mind. It assumes that it gets a HTTP request (only incoming data) and returns the response (only outgoing data). This makes it tricky to force django into the concept of a websocket where bidirectional communication is allowed.

What I'm doing in django-websocket to achieve this is that I dig very deeply into the internals of WSGI and django's request object to retrieve the underlaying socket. This tcp socket is then used to handle the upgrade the HTTP request to a websocket instance directly.

Now to your original question ...

I hope the above makes it obvious that when a websocket is established, there is no point in returning a HttpResponse. This is why you usually don't return anything in a view that is handled by django-websocket.

However I wanted to stick close to the concept of a view that holds the logic and returns data based on the input. This is why you should only use the code in your view to handle the websocket.

After you return from the view, the websocket is automatically closed. This is done for a reason: We don't want to keep the socket open for an undefined amount of time and relying on the client (the browser) to close it.

This is why you cannot access a websocket with django-websocket outside of your view. The file descriptor is then of course set to -1 indicating that its already closed.

Disclaimer

I explained above that I'm digging in the surrounding environment of django to get somehow -- in a very hackish way -- access to the underlaying socket. This is very fragile and also not supposed to work since WSGI is not designed for this! I also explained above that the websocket is closed after the view ends - however after the websocket closed down (AND closed the tcp socket), django's WSGI implementation tries to send a HTTP response - it doesn't know about websockets and thinks it is in a normal HTTP request-response cycle. But the socket is already closed an the sending will fail. This usually causes an exception in django.

This didn't affected my testings with the development server. The browser will never notice (you know .. the socket is already closed ;-) - but raising an unhandled error in every request is not a very good concept and may leak memory, doesn't handle database connection shutdown correctly and many athor things that will break at some point if you use django-websocket for more than experimenting.

This is why I would really advise you not to use websockets with django yet. It doesn't work by design. Django and especially WSGI would need a total overhaul to solve these problems (see this discussion for websockets and WSGI). Since then I would suggest using something like eventlet. Eventlet has a working websocket implementation (I borrowed some code from eventlet for the initial version of django-websocket) and since its just plain python code you can import your models and everything else from django. The only drawback is that you need a second webserver running just to handle websockets.

like image 124
Gregor Müllegger Avatar answered Sep 28 '22 03:09

Gregor Müllegger