<!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() |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
Thank you DawsonIp, I have the same error. |
This comment has been minimized.
This comment has been minimized.
@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. |
This comment has been minimized.
This comment has been minimized.
Nice example, but I'm getting this error: |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
Thanks for the update. What version of python is your example built for? |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
This comment has been minimized.
@SadatAnwar Your code is not available and receiving 404 now. |
This comment has been minimized.
This comment has been minimized.
@sivaa his code is available. url is slightly different https://gist.github.com/SadatAnwar/222d4643c25f72293461 . I think he misspelled his name |
This comment has been minimized.
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.).