Websockets give us the ability to send data either way, whenever we want.
def post(self):
user.update(self.request.body)
user.save(safe=True)
# gee, I hope someone refreshes...
var update = function() {
$.get("/users/foo", function(users) {
users.forEach(function(user) {
// compare every user to see if anything has
// changed, and update views accordingly
});
setTimeout(update, 3000);
})
};
def post(self):
user.update(self.request.body)
user.save(safe=True)
socket.broadcast("updated_user", user.to_dict())
// after we've connected to the websocket...
var onmessage = function(message) {
if (message.type == "updated_user") {
updateUser(user.id, new_user);
}
};
GET / HTTP/1.1
Host: www.myapp.com
Cookie: user=20a356ec01054c0b902fd265b7b1f5f4
If-None-Match: wetVrDrsf423tVSefsffv43SEf
User-Agent: Mozilla/11.0 (like Mosaic) BeOS/10.04
Accept: */*
HTTP/1.1 200 OK
Date: Thu, 24 Jan 2013 04:25:23 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 18
Set-Cookie: user=20a356ec01054c0b902fd265b7b1f5f4
ETag: wetVrDrsf423tVSefsffv43SEf
Cache-Control: private, max-age=0
...ALL DAT CONTENT!
...and the request hangs up. Probably.
GET /websocket HTTP/1.1
Host: myapp.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://myapp.com
Sec-WebSocket-Protocol: chat, uberchat
Sec-WebSocket-Version: 13
Upgrade: websocket
Connection: Upgrade
Sec-Websocket-Key
Sec-Websocket-Protocol
Sec-Websocket-Version
(Assuming success)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
... and stays open.
hashlib.sha1(raw).digest()
base64.b64encode(hash_digest)
...and you are powersliding down rainbows.
WebSockets send data across the wire in "frames"
Each frame conforms to a specific format
...not that you'll have to implement this.
Blocking servers are bad for WebSockets.
Alternate requests are handled in serial or by additional threads / processes
The server (or thread / process) can't ever release control.
from gevent import monkey
import socket
sock = socket.socket()
sock.connect(("myapp.com", 80))
sock.send("...")
sock.recv(4096)
# will block current process until finished
monkey.patch_all()
sock = socket.socket()
sock.connect(("myapp.com", 80))
sock.send("...")
sock.recv(4096)
# releases control to other I/O operations
from tornado.iostream import IOStream
from tornado.ioloop import IOLoop
import socket
def on_connect():
stream.write("...")
stream.read_bytes(4096, on_read)
def on_read(data):
# handle data
sock = socket.socket()
stream = IOStream(s)
stream.connect(("myapp.com", 80), on_connect)
IOLoop.instance().start()
Just for reference.
We can only send one direction at a time.
while True:
# we are going to wait until a message comes
packet = websocket.receive()
# now we can actually send a message
websocket.send(packet)
In the blocking style, it's easier to limit yourself to half-duplex.
def monitor_source():
while True:
# source can be twitter, redis, whatever
message = source.receive()
for subscriber in _SUBSCRIBERS:
subscriber.send(message)
def monitor_subscriber(websocket):
_SUBSCRIBERS.append(websocket)
while True:
message = websocket.receive()
# perform user action (update settings, broadcast, etc.)
gevent.spawn(monitor_source)
Same as your database, you want a central, shared message queue.
Use the phone, Josh.
IE | Chrome | Firefox | iOS | Android | |
---|---|---|---|---|---|
hixi-76 | 6 | 4 :( | 5 | ||
hybi-07 | 6 | ||||
hybi-10 | 14 | 7 | |||
RFC 6455 | 10 | 16 | 11 | 6 |
This depressing data brought to by Wikipedia.
@joshmarshall on the Twitters.
@joshmarshall on the GitHub.