Vinyll's blog

( Python, Javascript & Web stuff… )

Socket.io with gevent using Flask

Socketio is a html5-websockets/ajax/polling abstraction layer to connect realtime to a webserver. The purpose here is to see how to manage it with Python on server side. We'll use Flask to manage it as it will remain really basic.

This tutorial will show a realtime google Maps connection and we'll see friends connecting live.

Instead of writing a full tutorial, I propose to just get a very simple app and explain how it works, step by step.

Get the tutorial flask and socketio on github.

How it works

  1. The client renders a map, geo-localizes us and places a marker where we are. It informs the server of our position.
  2. The server replies giving us the position of all currently connected neighbors.
  3. The client shows them generating one marker for each neighbor.

Technical part

Server side / python

step 1 : init flask app, routes and server

from flask import Flask
app = Flask(__name__)

@app.route("/socket.io/<path:rest>")
def run_socketio(path):
    socketio_manage(request.environ, {'/default': DefaultNamespace})
    return ''

if __name__ == '__main__':
    server = SocketIOServer(('0.0.0.0', 8080), SharedDataMiddleware(app, {}),
        namespace="socket.io", policy_server=False)
    server.serve_forever()

Here we created a Flask app and a route to receive socketio url calls. That route will call the methods in the default Namespace.

Returning a empty string keeps Flask away from logging view errors.

Lastly, we create a SocketIO server and run it continuously at http://localhost:8000 (or using ip).

step 2 : init the socketio namespace class

The "/default" connection url we specified earlier will search for a DefaultNamespace class ("/other" url would refer to OtherNamespace). Therefore we can work in this namespace and create methods that will later be called by the client and respond to it. positions = {} class DefaultNamespace(BaseNamespace, RoomsMixin, BroadcastMixin): def on_localized(self, coords): self.socket.send_packet(dict(type="event", name='init_neighbors', args=positions, endpoint=self.ns_name)) id = self.socket.sessid positions[id] = coords self.broadcast_event_not_me('neighbor_localized', [id, coords])

The positions dict will store every connected user's id and coordinates. on_localized is an event receiver method. It will send the list of currently connected neighbors to the user who just logged, then inform neighbors that someone just connected.

Client side / javascript

step 1 : rendering Google Maps

function initialize() {
    map = new google.maps.Map(document.getElementById('map'),
        {zoom: 10, mapTypeId: google.maps.MapTypeId.ROADMAP});
    navigator.geolocation.getCurrentPosition(function(position) {
        var pos = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
        var marker = new google.maps.Marker({position:pos, map:map, title:"I'm here"});
        map.setCenter(pos);
        self.localized = true;
        self.position = position.coords;
        sendPosition();
  });
}
google.maps.event.addDomListener(window, 'load', initialize);

This piece of code actually renders the map and stores that we're localized and tries to send position to the server. For this to accomplish properly, we'll need code from step 2.

Here we assume that we have a html code like

<div id="map" style="width100%;height:200px"></div>

step 2 : send and receive socketio calls

    var map,
        socket = io.connect('http://localhost:8080/default'),
        connected = false, localized = false,
        position = {}, neighbors = {},
        self = this;

    //// Socket events ////
    socket.on('connect', function() {
        self.connected = true;
        sendPosition();
    });

    socket.on('init_neighbors', function(neighbors) {
        for(i in neighbors) {
            displayNeighbor(i, neighbors[i]);
        }
    });

    socket.on('neighbor_localized', function(args) {
       displayNeighbor(args[0], args[1])
    });

    function sendPosition() {
        if(!self.localized || !self.connected) return;
        socket.emit('localized', [self.position.latitude, self.position.longitude]);
    }

    function displayNeighbor(id, coords) {
        var marker = new google.maps.Marker({
            position: new google.maps.LatLng(coords[0], coords[1]),
            map: map,
            icon: 'http://google.com/mapfiles/ms/micons/yellow.png',
            title:"Neighbor"
        });
        self.neighbors[id] = marker;
    }

This is the longest code part then might deserve more explainations. In variables declaration, we wrote

io.connect('http://localhost:8080/default').

This is the very important part that will connect to the default url. Therefore will gain access to DefaultNamespace methods.

socket.on('connect', function()…

is kind of a magic event that will be called when the server informs the client that is got connected. We attempt here to send our position to the server, if position is avaible.

socket.on('init_neighbors')…

will be called from the server with the connected neighbors coordinates as argument.

function sendPosition()…

will send our position to the server if we are connected and geo-localized.

function displayNeighbor()…

drops a marker on the map.

Conclusion

The application is just a proof-of-concept. We should manage disconnection and more stuff for a realworld app. Yet it gives a good view of what and how we can do. I hope this allowed you to understand most of the usage of a python<>javascript websocket communication and feel free to contact me if you have any comment

If you notice any issue in this code, drop a ticket on github repository

Edit notes

It might be interesting to consider flask socket, an app Kenneth Reitz wrote late 2013.

sept. 2014: A new cool project has come out since this article: http://flask-socketio.readthedocs.org. I invite you to check it out if you work with Flask, gevent and SocketIO.

By vinyll on Sept. 30, 2012


Comments