Skip to content

@geoffb /simple_websocket_client.html

Embed URL


Subversion checkout URL

You can clone with
Download ZIP
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 = []

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.).


What does that lock in handle(client, addr) do? Isn't the point of a lock to synchronize several threads? Is that a bug for each thread to have its own lock?


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?


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.


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


@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.


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'


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


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


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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.