WebSockets to Socket.io
Join the DZone community and get the full member experience.
Join For FreeIn a previous blog post, I showed how you can use Gevent, ZeroMQ, WebSockets, and Flot to create a nice asynchronous server that graphs events in real time to multiple web consumers. Unfortunately, Chrome is just about the only browser that my demo worked in due to some issues with the WebSockets standard. So I've updated the example to use Socket.io, a WebSockets-like Javascript library that will work across multiple browsers.
Prerequisites
The best place to start is my last post; you'll
need all the libraries I mentioned there. In addition, you need to
install gevent-socketio for Socket.io support in gevent.
Changes from WebSockets server
The first thing we need to change is we need to create a SocketIOServer to replace our WSGIServer with a WebSocketHandler. While we're at it, we'll go ahead and merge our little static file server with the SocketIOServer. Our new main method now looks like this:
def main(): '''Set up zmq context and greenlets for all the servers, then launch the web browser and run the data producer''' context = zmq.Context() # zeromq: tcp to inproc gateway gevent.spawn(zmq_server, context) # WSGI server: copies inproc zmq messages to websocket sio_server = SocketIOServer( ('', 8000), SocketIOApp(context), resource="socket.io") # Start the server greenlets sio_server.start() # Open a couple of webbrowsers webbrowser.open('http://localhost:8000/graph.html') webbrowser.open('http://localhost:8000/graph.html') # Kick off the producer zmq_producer(context)
Now let's take a look at our SocketIOApp:
class SocketIOApp(object): '''Funnel messages coming from an inproc zmq socket to the socket.io''' def __init__(self, context): self.context = context self.static_app = paste.urlparser.StaticURLParser( os.path.dirname(__file__)) def __call__(self, environ, start_response): if not environ['PATH_INFO'].startswith('/socket.io'): return self.static_app(environ, start_response) socketio = environ['socketio'] sock = self.context.socket(zmq.SUB) sock.setsockopt(zmq.SUBSCRIBE, "") sock.connect('inproc://queue') while True: msg = sock.recv() socketio.send(msg)
What we've done here is overlay the StaticURLParser provided by paste with our own little ZeroMQ-to-Socket.io gateway. So if a request hits any path starting with /socket.io, it gets routed to our gateway. Otherwise it gets handled as a request for a static resource. We could have had a separate WSGIServer dedicated to handling static requests, but it seemed simpler (to me) to go ahead and combine them here.
Client Updates
Now, for our client, we've made a couple of changes as well. For one, we need to include the socket.io.js library. I've included the one from the socket.io CDN to simplify development:
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
In our javascript, I've also made a few changes to handle the
Socket.io protocol better as well as make the graph update a little less
choppy. First, we'll set up our socket and some basic variables we'll
use later:
$(function() { socket = new io.Socket('localhost'); socket.connect(); var $placeholder = $('#placeholder'); var datalen = 100; var plot = null; var series = { label: "Value", lines: { show: true, fill: true }, data: [] };
Next, we need to handle the protocol events from socket.io to update our status:
socket.on('connect', function() { $('#conn_status').html('<b>Connected: ' + socket.transport.type + '</b>'); }); socket.on('error', function() { $('#conn_status').html('<b>Error</b>'); }); socket.on('disconnect', function() { $('#conn_status').html('<b>Closed</b>'); });
Finally, we will handle the actual messages coming from the server:
socket.on('message', function(msg) { var d = $.parseJSON(msg); series.data.push([d.x, d.y]); while (series.data.length > datalen) { series.data.shift(); } if(plot == null && series.data.length > 10) { plot = $.plot($placeholder, [series], { xaxis:{ mode: "time", timeformat: "%H:%M:%S" }, yaxis: { min: 0, max: 5 }, hooks: { draw: function(plot, canvascontext) { // Redraw the graph in 50ms setTimeout(function() { plot.setData([series]); plot.setupGrid(); plot.draw();}, 50); } } }); } }); });
What we're doing here is pushing the value from the server on an array, shifting off the old values. Then we set up a hook for Flot
to redraw the graph every 50ms when drawing completes. Altogether,
there aren't too many changes from our WebSockets demo, and that demo
itself wasn't too complex. If you'd like to see all the code, you can at
my SourceForge Repo. Hope you've enjoed these posts as much as I've enjoyed playing around with these libraries. Happy hacking!
Source: http://blog.pythonisito.com/2011/08/websockets-to-socketio.html
Opinions expressed by DZone contributors are their own.
Comments