The future of illusively responsive web apps is here. Actually since December 2009 when Google released their 4.0.249.0 version of Chrome, WebSockets have been available to play around with. Connecting and communicating with a server from a regular web page is now as easy as:

var ws = new WebSocket("ws://localhost:9999/");
ws.onopen = function() {
  ws.send("Hello Mr. Server!");
};
ws.onmessage = function (e) { alert(e.data); };
ws.onclose = function() { };

This opens a bi-directional connection to a server, allowing messages to be sent between a client (i.e. the web page) and a WebSocket server (this example) without the overhead introduced by opening a new connection for every single call. Up until now, this kind of behavior could only be achieved by Ajax requests combined with various tricks. This in order to keep a session open and not close immediately after a message has been sent and received.

The result of this is blazingly fast communication between a server and one or many connected clients. Here’s a video I recorded where two audio clients, i.e. web pages with websockets, are connected to an audio server which is using the Spotify library to stream music. Look at how they’re really in sync! And remember that each command produced by moving the slider is sent to the server, parsed and then sent back to the other client. This almost without any noticeable lag:

There are already some projects out there that attempts to provide a server solution. Many of them exists merely to illustrate the concept, but we’ll definitely see some real implementations popping up very soon. The aim with this post was to do as little as possible in order to get a message sent from Chrome to pass through a Python socket setup and bounce back to the browser.

To get this working we need some background details to get the implementation right. Firstly, there is the WebSocket API Draft. This is what has been implemented in Chrome and specifies how to connect to the socket via Javascript on our web page:

Secondly there is the Websocket Protocol which is currently at the draft stage but has gone through 74 iterations as we’re speaking. The protocol tells us how to wrap and extract messages sent to and from our server. In the protocol it is stated that a header like the following raw text shall be sent from the client, in this case Chrome. So this is what we’ll recieve at the server socket:

GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample

Our server shall then consume this text, validate it, and answer by sending the following back to the client:

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: http://example.com
WebSocket-Location: ws://example.com/demo
WebSocket-Protocol: sample

If this so called handshake doesn’t look right to the client, it will close the connection. So it’s important that WebSocket-Origin and WebSocket-Location is copied from the first message exactly as stated. Otherwise Chrome will suspect that something is fishy and cut the cord.

And the code for out Python server looks like this:

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("", 9999))
sock.listen(5)

handshake = '\
HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
WebSocket-Origin: http://localhost:8888\r\n\
WebSocket-Location: ws://localhost:9999/\r\n\r\n\
'
handshaken = False

print "TCPServer Waiting for client on port 9999"

import sys

data = ''
header = ''

client, address = sock.accept()
while True:
    if handshaken == False:
        header += client.recv(16)
        if header.find('\r\n\r\n') != -1:
            data = header.split('\r\n\r\n', 1)[1]
            handshaken = True
            client.send(handshake)
    else:
            tmp = client.recv(128)
            data += tmp;

            validated = []

            msgs = data.split('\xff')
            data = msgs.pop()

            for msg in msgs:
                if msg[0] == '\x00':
                    validated.append(msg[1:])

            for v in validated:
                print v
                client.send('\x00' + v + '\xff')

As you might notice, I make use of the low-level socket library in Python. A message sent from a client is stripped from surrounding tags (00 and ff), and sent back with the same padding. Several messages can be read in the same 128 byte chunk and split into the correct amount of validated messages. The remaining bytes are saved until the next read.

This implementation is rather useless by itself. However, it demonstrates the Websocket which really has the potential to revolutionize future web apps. Hopefully by not getting more advanced than this, some basic understanding about the communication flow can be learned.

To get more inspiration for your Websocket implementation there are some nice code out there which will get you up and running with both Python and Node.js as backend


  • http://mikaelhalen.com Mikael Halén

    I read lots of articles and tutorials describing how to code stuff and all too often they are cluttered with a bunch of fancy stuff that isn’t necessary for the solution. By describing the websocket server in this minimalistic way makes it easy to understand and extend! Thanks for sharing!

  • http://popdevelop.com Johan Brissmyr

    Thanks! Not much functionality in this solution though. Currently I’m scanning the web for a nice Python Websocket server. Or I’ll write one myself. Really need it for a project

  • http://hackr.se Joel Larsson

    Nice tutorial, lets see if we can use this in dogvibes, http://hackr.se/code/dogvibes

    /Joel

  • http://www.deanbreaker.com/ Amy

    I read lots of articles and tutorials describing how to code stuff and all too often they are cluttered with a bunch of fancy stuff that isn’t necessary for the solution. By describing the websocket server in this minimalistic way makes it easy to understand and extend! Thanks for sharing!

  • Matthew

    Very good tutorial, but I ran into one minor issue. If you’re just pasting the JavaScript part into an HTML file and pointing your browser at it, it won’t work. You’ll need to change line 10 of the python script to:

    WebSocket-Origin: null\r\n\

    And if you’re running it from a webserver; that line has to show the correct address and port that the webserver is running on.

  • Ben

    Hi Eventlet now has support for websocket in core:

    http://eventlet.net/doc/examples.html#websocket-server-example

    And I’m currently adding suport for it to repoze.bfg in the rpz.websocket library:

    http://github.com/boothead/rpz.websocket

    The big advantage of eventlet is that the socket fiddling has already been taken car of to be non blocking, and the big advantage of repoze is that you can have a websocket connections available at the same url as the original representation you’re connecting from, i.e:

    You have a representation of a server at

    http://example.com/server1/
    then your websocket connection goes to ws://example.com/server1/ for the live streaming events related to the server (or whatever) represented by the /server1/ url.

    Cheers,
    Ben

  • MrGretchev

    Thank you so much. I’ve been playing with a few other examples, but as has been said before, have too much functionality for a simple demo. I just wanted to handshake and send data, which is what you do here!

    Thanks again!

  • Roger Erens

    You might make note that this code is a little outdated now that draft-74 has been superseded. Problem (at least for me) is to find out which browser-version is using which draft…

    Thanks for your example, though.

  • http://popdevelop.com Johan Brissmyr

    Yes, you’re right. The drafts are not yet stable. I’ll update the post and state the relevant draft version.

  • James Mills / prologic

    Based on the code found in this blog (and other reading material on the web) circuits.web (1) as of revision fe3965b21fe2 now implements a working WebSockets Server:

    https://bitbucket.org/prologic/circuits/changeset/fe3965b21fe2

    This allows you to have a WebSockets server that sits alongside your application server (no need for a separate server, etc). This will be integrated into the circuits.web namespace before the next release.

    cheers
    James

    1. http://bitbucket.org/prologic/circuits/

  • David Notaker112358

    Nice. One minor issue though: on some versions of python, the following section of code results in an EOL:

    handshake = ’  HTTP/1.1 101 Web Socket Protocol Handshakern  Upgrade: WebSocketrn  Connection: Upgradern  WebSocket-Origin: http://localhost:8888rn  WebSocket-Location: ws://localhost:9999/rnrn  ’ 

    The problem can be fixed by using triple quotes instead of end-of-line backslashes:

    handshake = ”’
    HTTP/1.1 101 Web Socket Protocol Handshakern 
    Upgrade: WebSocketrn 
    Connection: Upgradern 
    WebSocket-Origin: http://localhost:8888rn 
    WebSocket-Location: ws://localhost:9999/rnrn 
    ”’

  • Liam

    Is there any chance you can update this article, I can’t seem to get it working so I assume this is an old protocol. Been searching for months just for a really simple implementation fo WebSockets

  • Paul

    when I tried this I got headers like this:

    Sec-WebSocket-Version: 13
    Sec-WebSocket-Key: {—some-hash-value—}

    after some reserach I found that the proper handshake response must include:

    Sec-WebSocket-Accept: {–a-new-calculated-hash-value–}

    and you can calulate the ‘accept’ value with:

    key   = Sec_WebSocket_Key_value
    magic = ’258EAFA5-E914-47DA-95CA-C5AB0DC85B11′

    step1 = hashlib.sha1(key+magic)
    step2 = base64.b64encode(step1.digest())

    Sec_WebSocket_Accept_value = step2

     

  • Chris

    Above post refers to previous WebSocket specification