public

  • Download Gist
simple_websocket_client.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
<!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>
simple_websocket_server.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
#!/usr/bin/env python
 
import socket, struct, hashlib, threading, cgi, base64
 
def create_hash (key):
guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
sha1 = hashlib.sha1(key + guid)
digest = sha1.digest()
return base64.b64encode(digest)
 
def recv_data (client, length):
data = client.recv(length)
 
# Decode the websocket frame.
 
# Length is the first byte.
length = (struct.unpack("B", data[1:2])[0]) & 127
 
# Determine where the mask begins.
maskStart = 2
if length == 126:
# Following two bytes are the length
maskStart = 4
elif length == 127:
# Following eight bytes are the length
maskStart = 10
 
dataStart = maskStart + 4
length = len(data) - dataStart
masks = data[maskStart:maskStart + 4]
 
print "data length:", len(data)
print "maskStart:", maskStart
print "dataStart:", dataStart
print "message length:", length
 
# Decode the data itself.
j = 0
decodedData = ""
for i in range(dataStart, len(data)):
val = struct.unpack("B", data[i:i + 1])[0]
m = struct.unpack("B", masks[j:j + 1])[0]
val = val ^ m
j = (j + 1) % 4
decodedData += str(unichr(val))
 
print "decoded:", decodedData
return decodedData
 
def send_data (client, data):
# Encode the data into the websocket frame format.
# HACK: First byte is always 129 because we assume we're always sending text.
message = chr(129)
length = len(data)
if length <= 125:
# Length fits into one byte.
message = message + chr(length)
elif length >= 126 & length <= 65535:
# Length fits into two bytes.
message = message + chr(126)
message = message + chr((length >> 8) & 255)
message = message + chr(length & 255)
else:
# Length goes into eight bytes.
message = message + chr(127)
message = message + chr((length >> 56) & 255)
message = message + chr((length >> 48) & 255)
message = message + chr((length >> 40) & 255)
message = message + chr((length >> 32) & 255)
message = message + chr((length >> 24) & 255)
message = message + chr((length >> 16) & 255)
message = message + chr((length >> 8) & 255)
message = message + chr(length & 255)
 
# Now add the data.
message = message + data
 
client.send(message)
 
def parse_headers (data):
headers = {}
lines = data.splitlines()
for l in lines:
parts = l.split(": ", 1)
if len(parts) == 2:
s = parts[1].strip()
headers[parts[0]] = s
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-Key']
)
shake = "HTTP/1.1 101 Switching Protocols\r\n"
shake += "Upgrade: websocket\r\n"
shake += "Connection: Upgrade\r\n"
shake += "Sec-WebSocket-Accept: " + digest + "\r\n\r\n"
print shake
return client.send(shake)
 
def handle (client, addr):
handshake(client)
lock = threading.Lock()
while 1:
data = recv_data(client, 1024)
if not data: break
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()

Hi, I have a question: When you make a new lock within the 'handle' function, aren't you making new locks for every new Thread (that is, N locks for N threads)?
Wouldn't it be better to make a new Lock inside the 'start_server' function and make it shared among all the Threads?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.