2014-02-10T03:46:26Z

Easy WebSockets with Flask and Gevent

This weekend I decided to take a short vacation from my book writing effort and spend time on a project I wanted to work on for a long time. The result of this effort is a brand new Flask extension that I think is pretty cool.

I'm happy to introduce Flask-SocketIO, a very easy to use extension that enables WebSocket communications in Flask applications.

What is WebSocket?

WebSocket is a new communication protocol introduced with HTML5, mainly to be implemented by web clients and servers, though it can also be implemented outside of the web.

Unlike HTTP connections, a WebSocket connection is a permanent, bi-directional communication channel between a client and the server, where either one can initiate an exchange. Once established, the connection remains available until one of the parties disconnects from it.

WebSocket connections are useful for games or web sites that need to display live information with very low latency. Before this protocol existed there were other much less efficient approaches to achieve the same result such as Comet.

The following web browsers support the WebSocket protocol:

  • Chrome 14
  • Safari 6
  • Firefox 6
  • Internet Explorer 10

What is SocketIO?

SocketIO is a cross-browser Javascript library that abstracts the client application from the actual transport protocol. For modern browsers the WebSocket protocol is used, but for older browsers that don't have WebSocket SocketIO emulates the connection using one of the older solutions, the best one available for each given client.

The important fact is that in all cases the application uses the same interface, the different transport mechanisms are abstracted behind a common API, so using SocketIO you can be pretty much sure that any browser out there will be able to connect to your application, and that for every browser the most efficient method available will be used.

What about Flask-Sockets?

A while ago Kenneth Reitz published Flask-Sockets, another extension for Flask that makes the use of WebSocket accessible to Flask applications.

The main difference between Flask-Sockets and Flask-SocketIO is that the former wraps the native WebSocket protocol (through the use of the gevent-websocket project), so it can only be used by the most modern browsers that have native support. Flask-SocketIO transparently downgrades itself for older browsers.

Another difference is that Flask-SocketIO implements the message passing protocol exposed by the SocketIO Javascript library. Flask-Sockets just implements the communication channel, what is sent on it is entirely up to the application.

Flask-SocketIO also creates an environment for event handlers that is close to that of regular view functions, including the creation of application and request contexts. There are some important exceptions to this explained in the documentation, however.

A Flask-SocketIO Server

Installation of Flask-SocketIO is very simple:

$ pip install flask-socketio

Below is an example Flask application that implements Flask-SocketIO:

from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('my event')
def test_message(message):
    emit('my response', {'data': message['data']})

@socketio.on('my broadcast event')
def test_message(message):
    emit('my response', {'data': message['data']}, broadcast=True)

@socketio.on('connect')
def test_connect():
    emit('my response', {'data': 'Connected'})

@socketio.on('disconnect')
def test_disconnect():
    print('Client disconnected')

if __name__ == '__main__':
    socketio.run(app)

The extension is initialized in the usual way, but to simplify the start up of the server a custom run() method is used instead of flask run or app.run(). This method starts the eventlet or gevent servers if they are installed. Using gunicorn with the eventlet or gevent workers should also work. The run() method takes optional host and port arguments, but by default it will listen on localhost:5000 like Flask's development web server.

The only traditional route in this application is /, which serves index.html, a web document that contains the client implementation of this example.

To receive WebSocket messages from the client the application defines event handlers using the socketio.on decorator.

The first argument to the decorator is the event name. Event names 'connect', 'disconnect', 'message' and 'json' are special events generated by SocketIO. Any other event names are considered custom events.

The 'connect' and 'disconnect' events are self-explanatory. The 'message' event delivers a payload of type string, and the 'json' and custom events deliver a JSON payload, in the form of a Python dictionary.

To send events a Flask server can use the send() and emit() functions provided by Flask-SocketIO. The send() function sends a standard message of string or JSON type to the client. The emit() function sends a message under a custom application-defined event name.

Messages are sent to the connected client by default, but when including the broadcast=True optional argument all clients connected to the namespace receive the message.

A SocketIO Client

Ready to try your hand at some Javascript? The index.html page used by the example server contains a little client application that uses jQuery and SocketIO. The relevant code is shown below:

$(document).ready(function(){
    var socket = io();
    socket.on('my response', function(msg) {
        $('#log').append('<p>Received: ' + msg.data + '</p>');
    });
    $('form#emit').submit(function(event) {
        socket.emit('my event', {data: $('#emit_data').val()});
        return false;
    });
    $('form#broadcast').submit(function(event) {
        socket.emit('my broadcast event', {data: $('#broadcast_data').val()});
        return false;
    });
});

The socket variable is initialized with a SocketIO connection to the server. If the Socket.IO server is hosted at a different URL than the HTTP server, then you can pass a connection URL as an argument to io().

The socket.on() syntax is used in the client side to define an event handler. In this example a custom event with name 'my response' is handled by adding the data attribute of the message payload to the contents of a page element with id log. This element is defined in the HTML portion of the page.

The next two blocks override the behavior of two form submit buttons so that instead of submitting a form over HTTP they trigger the execution of a callback function.

For the form with id emit the submit handler emits a message to the server with name 'my event' that includes a JSON payload with a data attribute set to the value of the text field in that form.

The second form, with id broadcast does the same thing, but sends the data under a different event name called 'my broadcast event'.

If you now go back to the server code you can review the handlers for these two custom events. For 'my event' the server just echoes the payload back to the client in a message sent under event name 'my response', which is handled by showing the payload in the page. The event named 'my broadcast event' does something similar, but instead of echoing back to the client alone it broadcasts the message to all connected clients, also under the 'my response' event.

You can view the complete HTML document in the GitHub repository.

Running the Example

To run this example you first need to download the code from GitHub. For this you have two options:

The example application is in the example directory, so cd to it to begin.

To keep your global Python interpreter clean it is a good idea to make a virtual environment:

$ virtualenv venv
$ . venv/bin/activate

Then you need to install the dependencies:

(venv) $ pip install -r requirements.txt

Finally you can run the application:

(venv) $ python app.py

Now open your web browser and navigate to http://localhost:5000 and you will get a page with two forms as shown in the following screenshot:

Any text you submit from any of the two text fields will be sent to the server over the SocketIO connection, and the server will echo it back to the client, which will append the message to the "Receive" part of the page, where you can already see the message sent by the 'connect' event handler from the server.

Things get much more interesting if you connect a second browser to the application. In my case I'm testing this with Firefox and Chrome, but any two browsers that you run on your machine will do. If you prefer to access the server from multiple machines you can do that too, but you first need to change the start up command to socketio.run(app, host='0.0.0.0') so that the server listens on the public network interface.

With two or more clients when you submit a text from the form on the left only the client that submitted the message gets the echoed response. If you submit from the form on the right the server broadcasts the message to all connected clients, so all get the reply.

If a client disconnects (for example if you close the browser window) the server will detect it a few seconds later and send a disconnect event to the application. The console will print a message to that effect.

Final Words

For a more complete description of this extension please read the documentation. If you want to make improvements to it feel free to fork it and then submit a pull request.

I hope you make cool applications with this extension. I can tell you that I had a lot of fun implementing this extension.

If you make something with it feel free to post links in the comments below.

Miguel

495 comments

  • #376 yuvi said 2017-08-23T10:48:56Z

    Hi, you got any code example for user and bot communication through socketio. i am trying to create multiuser chat application which communicates with the BOT and return realtime replies. can you guide me here.

  • #377 Miguel Grinberg said 2017-08-23T14:44:04Z

    @yuvi: I don't have anything that includes a bot, but for a simple chat example, see https://github.com/miguelgrinberg/Flask-SocketIO-Chat. The bot can be implemented as a client, or can also be embedded in the server. If you take the bot as a client route, you need to use a Socket.IO client to send messages, such as https://pypi.python.org/pypi/socketIO-client-2.

  • #378 ella ramos said 2017-12-27T00:03:37Z

    Hello Miguel,

    Thank you for this tutorial. I am trying to understand how sockets work because I am planning to use this for my flask and mqtt project. What is the difference between running socketio.run(app) vs. app.run(app).

    My flask app will have mqtt integration, so does this gives me separate threads when handling mqtt messages while still serving web pages?

  • #379 Miguel Grinberg said 2017-12-28T07:16:08Z

    @ella: the socket.run() call adds the WebSocket server on top of the regular Flask server. I don't really know much about mqtt, but Flask-SocketIO is not about giving you thread support. If you need background threads, you can create them in your application.

  • #380 Srushti Gangireddy said 2018-01-11T00:16:36Z

    Hello Miguel, I am currently working on a flask web application which is deployed and running. Can i include only the socket-io part to the already running flask application. The actual problem i am trying to solve is to add the chat capability to the website. Please let me know if there is any possible way to do this. Thanks and Regards, Srushti.

  • #381 Miguel Grinberg said 2018-01-11T02:55:50Z

    @Srushti: I don't understand your question. Flask-SocketIO doesn't have anything else besides the "SocketIO part".

  • #382 Vishwajeet Srivastava said 2018-01-15T07:00:24Z

    Hello @Miguel,

    I think what @Srushti is trying to say is that she already have a web app which is running on flask.

    Now she wants to include Chat functionality in the web-app and she wants to know that if she can use Flask-SocketIO along with her existing Web-app side by side Or she needs to create a separate project for Chat and deploy it separately, possibly on some other port?

    Actually this my questions as well.

    Thanks

    Regards, VJ

  • #383 Miguel Grinberg said 2018-01-15T07:34:59Z

    @Vishwajeet: Yes, you can add SocketIO to an existing application. The example in this article presents a Flask application that has been extended to also support Socket.IO events.

  • #384 Rakin said 2018-02-03T17:15:52Z

    Hello Miguel,

    I have made a real time chat application following Your official documentation of Flask-SocketIO. Regarding the project I have basically 3 questions for you including some criteria which is unclear to me:

    1) As per the documentation to enable asynchronous service I decided to use gevent and I used monkey.patch_all() in my project. But, is there any method to verify that my project is actually using gevent or not ?

    2) In the Nginx section of the official doc there is a separate portion for " location /socket.io " configuration. What is the role of it ? In my running server I checked the url -- http://my_real_ip:5000/socket.io ; but I found nothing. Then why I have to configure the specified route in Nginx ?

    3) I have intended to deploy the chat application using uWsgi or Gunicorn Server with multiple workers behind Nginx. So, I followed the official doc but couldn't co-relate some configurations and thus resulting failure in production deployment. So, I feel that a complete guide for production deployment using uWsgi/Gunicorn behind Nginx will be so much conducive to reproduce. P.S:- I have already followed some tutorials in Digital Ocean... but I think those are less specific for flask-socketIO thus generating errors like IOError: Unable to complete WebSocket Handshake.... etc.

    Thanks.... Rakin

  • #385 Miguel Grinberg said 2018-02-03T22:49:21Z

    @Rakin: 1) if you have gevent and gevent-websocket installed, then gevent will be used automatically. You can see that it is in use by looking at the logs. 2) the Socket.IO protocol uses this URL. 3) Not sure I understand what the problem was. The documentation has all the information required to deploy nginx+gunicorn. Maybe you need to ask a specific question about the errors that you are seeing?

  • #386 rilwan said 2018-02-07T10:47:48Z

    I have python flask at server end and js on client side. I want to send some logs to client without a request from client. I am trying to implement it with flask socket.io plugin.

    Everything looks OK, thanks for below blogs. https://blog.miguelgrinberg.com/post/easy-websockets-with-flask-and-gevent

    But I can see regular polling going to server and it takes too long to respond. This polling delays my other AJAX calls too. What could be reason.

    server side-

    @socketio.on('connect', namespace='/test') def connect(): global thread with thread_lock: if thread is None: thread = socketio.start_background_task(target=background_thread) socketio.emit('server_msg', {'cause': 'connection', 'status': 'Success', 'count': len(CLIENT.keys())}, namespace='/test')

    @socketio.on('client_msg', namespace='/test') def client_msg(msg): print(msg) # TODO: process this msg

    def background_thread(): count = 0 while True: socketio.sleep(5) count += 1 socketio.emit('my_response', {'data': 'Server generated event', 'count': count}, namespace='/test') Clinet side -

    namespace = '/test'; var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);

    socket.on('connect', function () { socket.emit('client_msg', {cause: 'connection', details: location}); }); socket.on('server_msg', function (msg) { writeLog('log', 'Received ' + msg.cause + ' ' + msg.status) }); socket.emit('client_msg', {cause: 'connection', details: location});

  • #387 Miguel Grinberg said 2018-02-09T21:27:36Z

    @rilwan: You need to use an async framework, either gevent or eventlet.

  • #388 Frank said 2018-03-28T05:17:08Z

    Hello Miguel, I have observed a peculiar behaviour with forms when using socketio: consider this form: Normally when any of the submit buttons is clicked in a normal HTTP request a different value is returned. This does not happen with socketIO. You need a different form name and a different id to get the appropriate value. This makes the html template very verbose and also needs a different def in the app.py Is there a way around it? I must admit I not good at all with JS and JQuery Many thanks Frank

  • #389 Miguel Grinberg said 2018-03-29T05:09:15Z

    @Frank: I'm afraid you are going to need some JS/jQuery support for this. Using the same id on multiple elements on the page isn't really that useful, you might as well not use ids at all in that case. Your jQuery event handler receives an event object that has the element that was clicked, so you can then read the value from it. Using a unique id is a convenience thing, but that is not the only way.

  • #390 Lautaro Ariel Araujo said 2018-04-23T23:21:18Z

    Thanks a lot for your tutorials Miguel!!! I'm so glad i found your blog.

  • #391 Hua Gong said 2018-05-16T04:20:59Z

    Thanks for this tutorial. Your blog has helped me a lot on learning flask.

    Following your example, I have implement websocket in my web app. But how do I test these websockets? There is not much out there that talks about testing websockets. I used selenium to test my app from front end, but it was unsuccessful with websocket.

  • #392 Miguel Grinberg said 2018-05-16T04:42:26Z

    @Hua: My Flask-SocketIO extension now has a test client that is similar in purpose to Flask's own, but designed to help you test your SocketIO events.

  • #393 radicz said 2018-05-26T21:12:20Z

    Hey Miguel, at first thanks for this lib!

    I have a question about nesting @socketio.on event handler into function.

    If I write something like this:

    def my_func(): @socketio.on("another_event", namespace="/test") def another_callback(message): # some other code here

    how do I properly end my_func() from the inner callback? thanks in advance

  • #394 Miguel Grinberg said 2018-05-26T22:50:17Z

    @radicz: not sure I understand your question. What you are doing is unusual, but there is nothing special about it. As long as you make sure my_func() is called during your application's initialization the event will be registered and should work just fine. If the function is called after the server starts then that could be a problem, the event handlers need to be registered before the server is started.

  • #395 rajj said 2018-06-10T20:27:46Z

    Hi Miguel,

    I tried to fetch video stream from webcam using webrtc and sending video stream to backend(python), process with opencv and return video frame back to frontend using flask gevent-websocket and its working, but when tried with flask-socketio the same thing its not working, can you help. below is my github link:- https://github.com/singhrajesh8699/flask_geventwebsocket_videostreaming. Thank you for your responses.

  • #396 Miguel Grinberg said 2018-06-11T04:02:56Z

    @rajj: if you just tell me that "it does not work" without providing any details I have no way to help you. The link that you provided shows a project that does not use Flask-SocketIO, so it seems unrelated to your problem.

  • #397 yuhan cheng said 2018-07-27T13:21:34Z

    Hi Miguel, Thank you for the great tutorial!

    I have a question about how to use socketio.run() to replace app.run() of the Microblog project. // I want to build a websocket server on the Microblog

    ( in Microblog project) I tried to add socketio.run(app) in < microblog.py > and an error happened following:

    File "......./venv/lib/python3.6/site-packages/flask_socketio/__init__.py", line 490, in run if self.server.eio.async_mode == 'threading': AttributeError: 'NoneType' object has no attribute 'eio'

    Thank you for your attention. Thanks for a nice help!!

  • #398 yuhan cheng said 2018-07-27T15:12:49Z

    Hi Miguel, Thank you for the great tutorial! Sorry, the last comment(problem) is not a real problem, just my careless fault.

    The real problem is how do I use socketio.run() replace app.run() of the Microblog project correctly. And is it right to add the socketio decorator into ? @socketio.on('connect', namespace='/test') def test_connect(): emit('my response', {'data': 'Connected'})

    Is it available to build a websocket server on the Microblog, and let another server send msg to browser using the room function?

    ( in Microblog project) I tried to add socketio.run(app) in < app/init.py > :

    from flask_socketio import SocketIO socketio = SocketIO() ... def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) db.init_app(app) ... if not app.debug and not app.testing: ... socketio.run(app) return socketio

    The original routes and functions of microblog is ok, but the socket is not work. Thank you for your attention. Thanks for the nice help!!hero!!

  • #399 Miguel Grinberg said 2018-07-27T15:44:32Z

    @yuhan: have you initialized the extension? Looks like init_app() was not called.

  • #400 Miguel Grinberg said 2018-07-27T15:46:50Z

    @yuhan: I suggest you look at how I structure larger applications that also use Socket.IO. For example, take a look at https://github.com/miguelgrinberg/Flask-SocketIO-Chat.

Leave a Comment