-
-
Save geoffb/616117 to your computer and use it in GitHub Desktop.
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>WebSocket Client</title> | |
<style> | |
#output { | |
border: solid 1px #000; | |
} | |
</style> | |
</head> | |
<body> | |
<form id="form"> | |
<input type="text" id="message"> | |
<button type="submit">Send</button> | |
</form> | |
<hr> | |
<div id="output"></div> | |
<script> | |
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:", e.data); | |
var p = document.createElement("p"); | |
p.innerHTML = e.data; | |
output.appendChild(p); | |
}; | |
s.onerror = function (e) { | |
console.log("Socket error:", e); | |
}; | |
} catch (ex) { | |
console.log("Socket exception:", ex); | |
} | |
form.addEventListener("submit", function (e) { | |
e.preventDefault(); | |
s.send(inputBox.value); | |
inputBox.value = ""; | |
}, false) | |
</script> | |
</body> | |
</html> |
#!/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( | |
headers['Sec-WebSocket-Key1'], | |
headers['Sec-WebSocket-Key2'], | |
headers['code'] | |
) | |
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): | |
handshake(client) | |
lock = threading.Lock() | |
while 1: | |
data = recv_data(client, 1024) | |
if not data: break | |
data = cgi.escape(data) | |
lock.acquire() | |
[send_data(c, data) for c in clients] | |
lock.release() | |
print 'Client closed:', addr | |
lock.acquire() | |
clients.remove(client) | |
lock.release() | |
client.close() | |
def start_server (): | |
s = socket.socket() | |
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
s.bind(('', 9876)) | |
s.listen(5) | |
while 1: | |
conn, addr = s.accept() | |
print 'Connection from:', addr | |
clients.append(conn) | |
threading.Thread(target = handle, args = (conn, addr)).start() | |
clients = [] | |
start_server() |
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\threading.py", line 810, in __bootstrap_inner
self.run()
File "E:\Program Files (x86)\Python27\lib\threading.py", line 763, in run
self.__target(_self.__args, *_self.__kwargs)
File "E:\James\EclipseWorkspace - Kepler\ProjectDungeon\WebSocketServe\main.py", line 62, in handle
handshake(client)
File "E:\main.py", line 48, in handshake
headers['Sec-WebSocket-Key1'],
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 https://gist.github.com/ccheaton for making it possible.
My working code is available at https://gist.github.com/sadatanwer/222d4643c25f72293461
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.
@SadatAnwar Your code is not available and receiving 404 now.
@sivaa his code is available. url is slightly different https://gist.github.com/SadatAnwar/222d4643c25f72293461 . I think he misspelled his name
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.).