"Easy WebSockets with Flask and Gevent" translation learning notes -- Flask WebSocket Quick Start, websocketsflask
Link: http://blog.miguelgrinberg.com/post/easy-websockets-with-flask-and-gevent
I won't go over the introduction.
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 ServerFlask-SocketIO server code
Installation of Flask-SocketIO is very simple:
Install Flask-SocketIO using pip
$ pip install flask-socketio
Note that Flask-SocketIO depends on gevent, so at this time it can only run on Python 2. x. support for Python 3 is coming for gevent, so the situation is likely to improve in the near future. (Update: Flask-SocketIO version 1.0 is fully compatible with Python 2.7 and 3.3 +, see bottom of the article for more information ).
The latest status is that Flask-SocketIO v1.0 can run on Python 2.7 and Python 3.3 +.
Below is an example Flask application that implements Flask-SocketIO:
The following is a simple Flask application that implements Flask-SocketIO.
from flask import Flask, render_templatefrom flask.ext.socketio import SocketIO, emitapp = Flask(__name__)app.config['SECRET_KEY'] = 'secret!'socketio = SocketIO(app)@app.route('/')def index(): return render_template('index.html')@socketio.on('my event', namespace='/test')def test_message(message): emit('my response', {'data': message['data']})@socketio.on('my broadcast event', namespace='/test')def test_message(message): emit('my response', {'data': message['data']}, broadcast=True)@socketio.on('connect', namespace='/test')def test_connect(): emit('my response', {'data': 'Connected'})@socketio.on('disconnect', namespace='/test')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 provided by the extension. This method starts gevent, the only supported web server. Using gunicorn with a gevent worker shocould also work.
run()
Method takes optional
host
And
port
Arguments, but by default it will listen on
localhost:5000
Like Flask's development web server. the initialization method of extended (socketio) is the same as that of General initialization (that is, socketio = SocketIO (app). In the SocketIO program, run () by calling the member function () to start the server (that is, socketio. run (app), this method starts gevent (a module in Python that provides concurrent support, implemented through Management Switch greelet
CoroutineTo achieve concurrency ). You can also use gunicorn and gevent worker. The run () function has optional parameters such as host and argument. By default, the server that is implemented by Flask listens to localhost: 5000.
The only traditional route in this application is/
, Which servesindex.html
, A web document that contains the client implementation of this example.
To receive WebSocket messages from the client the application defines event handlers usingsocketio.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.'message'
Event delivers a payload of type string, and'json'
And custom events deliver a JSON payload, in the form of a Python dictionary.
In the above Code, the route of the application is only/, and the webpage index.html is returned at any time, includingClient implementation code.
The first parameter of the modifier isEvent name, 'Connect ', 'disconnect', 'message', 'json', and other special event names are defined by SocketIO, and other event names are custom events.
'Connect'AndNeedless to say about the 'disconnect' event,'Message'The event sends a string-type content. The 'json' event and Custom Event send a json content in the form of a Python dictionary.
Events can be defined inside a namespace by addingnamespace
Optional argument tosocketio.on
Decorator. Namespaces allow a client to open multiple connections to the server that are multiplexed on a single socket. When a namespace is not specified the events are attached to the default global namespace.
By adding the namespace optional parameter to the socketio. on modifier, you can define an event in a namespace. The namespace allows a client to repeatedly open multiple connections to the server on a socket connection. If a namespace is not specified for an event, the event is associated with the default global namespace.
To send events a Flask server can usesend()
Andemit()
Functions provided by Flask-SocketIO.send()
Function sends a standard message of string or JSON type to the client.emit()
Function sends a message under a custom event name.
Flask-SocketIO provides the Flask server with the send () and emit () functions to send events. The send () function sends a standard message of string or JSON type to the client, emit () the function sends a message under the Custom Event name to the client-for example, emit ('My response', {'data': message ['data']}) in the code. is to send a message for the 'my response' event, the message content subject is {'data': message ['data']}
Messages are sent to the connected client by default, but when includingbroadcast=True
Optional argument all clients connected to the namespace receive the message.
By default, a message is sent to a client that has been connected (and communicates at this time). However, if the optional parameter and value broadcast = True are included, the message is sent to all clients connected to the namespace.
Client code of A SocketIO ClientSocketIO
Ready to try your hand at some Javascript? Theindex.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.connect('http://' + document.domain + ':' + location.port + '/test'); 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; });});
Thesocket
Variable is initialized with a SocketIO connection to the server. Note how the namespace/test
Is specified in the connection URL. To connect without using a namespace it is sufficient to callio.connect()
Without any arguments.
Thesocket.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 addingdata
Attribute of the message payload to the contents of a page element with idlog
. 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 idemit
The submit handler emits a message to the server with name'my event'
That includes des a JSON payload withdata
Attribute set to the value of the text field in that form.
The second form, with idbroadcast
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.'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'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:
- Clone the repository with git
- Download the project as a zip file.
The example application is inexample
Directory, socd
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 navigatehttp://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'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 commandsocketio.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.
UPDATE: Flask-SocketIO version 0.3 adds supportRooms. This enables targeting subsets of connected users without having to use the broadcast option that sends to everyone.
UPDATE #2: Flask-SocketIO version 1.0 adds support for Python 3.3 +, and offers the choice to use gevent or eventlet as web server, as well as the standard Flask server. when using the Flask server the bi-directional channel is emulated using the HTTP long-polling technique.
Miguel