Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Super simple websockets client/server using Python. Compatible with the draft 76 challenge/response.
<!DOCTYPE html>
<html lang="en">
<title>WebSocket Client</title>
#output {
border: solid 1px #000;
<form id="form">
<input type="text" id="message">
<button type="submit">Send</button>
<div id="output"></div>
var inputBox = document.getElementById("message");
var output = document.getElementById("output");
var form = document.getElementById("form");
try {
var host = "ws://" + window.location.hostname + ":9876/stuff";
console.log("Host:", host);
var s = new WebSocket(host);
s.onopen = function (e) {
console.log("Socket opened.");
s.onclose = function (e) {
console.log("Socket closed.");
s.onmessage = function (e) {
console.log("Socket message:",;
var p = document.createElement("p");
p.innerHTML =;
s.onerror = function (e) {
console.log("Socket error:", e);
} catch (ex) {
console.log("Socket exception:", ex);
form.addEventListener("submit", function (e) {
inputBox.value = "";
}, false)
#!/usr/bin/env python
import socket, struct, hashlib, threading, cgi
def decode_key (key):
num = ""
spaces = 0
for c in key:
if c.isdigit():
num += c
if c.isspace():
spaces += 1
return int(num) / spaces
def create_hash (key1, key2, code):
a = struct.pack(">L", decode_key(key1))
b = struct.pack(">L", decode_key(key2))
md5 = hashlib.md5(a + b + code)
return md5.digest()
def recv_data (client, length):
data = client.recv(length)
if not data: return data
return data.decode('utf-8', 'ignore')
def send_data (client, data):
message = "\x00%s\xFF" % data.encode('utf-8')
return client.send(message)
def parse_headers (data):
headers = {}
lines = data.splitlines()
for l in lines:
parts = l.split(": ", 1)
if len(parts) == 2:
headers[parts[0]] = parts[1]
headers['code'] = lines[len(lines) - 1]
return headers
def handshake (client):
print 'Handshaking...'
data = client.recv(1024)
headers = parse_headers(data)
print 'Got headers:'
for k, v in headers.iteritems():
print k, ':', v
digest = create_hash(
shake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
shake += "Upgrade: WebSocket\r\n"
shake += "Connection: Upgrade\r\n"
shake += "Sec-WebSocket-Origin: %s\r\n" % (headers['Origin'])
shake += "Sec-WebSocket-Location: ws://%s/stuff\r\n" % (headers['Host'])
shake += "Sec-WebSocket-Protocol: sample\r\n\r\n"
shake += digest
return client.send(shake)
def handle (client, addr):
lock = threading.Lock()
while 1:
data = recv_data(client, 1024)
if not data: break
data = cgi.escape(data)
[send_data(c, data) for c in clients]
print 'Client closed:', addr
def start_server ():
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 9876))
while 1:
conn, addr = s.accept()
print 'Connection from:', addr
threading.Thread(target = handle, args = (conn, addr)).start()
clients = []
Copy link

clayheaton commented Oct 22, 2013

This is helpful for me because I'm trying to learn websockets. A few comments at the top of each file also would help (ports, etc.).

Copy link

dawsonlp commented Jan 31, 2014

Running Ubuntu/Chromium/31.0.1650.63, the headers that come from the browser when I load the webpage don't include Sec-WebSocket-Key1 and Sec-WebSocket-Key2, they only have Sec-WebSocket-Key, so the python code throws a KeyError exception in handshake(client) when it tries to build a digest.
Is this because of a recent change in Chrome? Has the websocket protocol been updated since this code was published?

Copy link

dawsonlp commented Jan 31, 2014

Explanation - the draft 76 challenge/response that this implements has been replaced with RFC-6455. Modern browsers don't work with this code as is.

Copy link

Paraintom commented Apr 24, 2014

Thank you DawsonIp, I have the same error.
Did you find a workaround?

Copy link

jclounge commented Jul 24, 2014

@brandonhsiao, yeah, it looks like there should instead only be a single lock. Then the threads would all use that one, so that only one thread at a time can do the send_data() calls.

Copy link

jamesbaber commented Jan 31, 2015

Nice example, but I'm getting this error:
Exception in thread Thread-2:
Traceback (most recent call last):
File "E:\Program Files (x86)\Python27\lib\", line 810, in __bootstrap_inner
File "E:\Program Files (x86)\Python27\lib\", line 763, in run
self.__target(_self.__args, *_self.__kwargs)
File "E:\James\EclipseWorkspace - Kepler\ProjectDungeon\WebSocketServe\", line 62, in handle
File "E:\", line 48, in handshake
KeyError: 'Sec-WebSocket-Key1'

Copy link

SadatAnwar commented Jul 28, 2015

A lot of the users complained that the code was throwing errors I needed it and too found that it was broken on this day (July 28, 2015) so I did my best to make it working again. Obviously all original credits go to for making it possible.

My working code is available at

Copy link

ptinker56 commented Sep 3, 2015

Thanks for the update. What version of python is your example built for?

Copy link

ptinker56 commented Sep 24, 2015

One comment on the newer code. Although the incorrect "recv(4096)" has been replaced, it still issues statements like "recv(4)" and assumes that 4 bytes were read. This is not true! The socket may have any number of bytes ready for recv, and you need to check the return value from recv to see how many bytes were actually transferred. If only two bytes have been received by the socket when recv is called, recv(-- anything greater than 2 --) will only return 2 bytes. You need a loop that keeps reading until the anticipated number of bytes have been read.

Copy link

sivaa commented Jan 9, 2017

@SadatAnwar Your code is not available and receiving 404 now.

Copy link

Benargee commented Oct 30, 2017

@sivaa his code is available. url is slightly different . I think he misspelled his name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment