Because it became necessary to implement a chat application by accident I investigated how I could implement a WebSocket application using Flask.
I will write up to the point where I try to move the sample This alone is enough to use the main functions of chat.
I'm going to implement it on Flask's shoulders The following two extensions were listed as options.
It's a little old as February 2014, but in Explanatory article by miguelgrinberg There is a section that explains the comparison between the two.
++++++++++++++++++++++++++++++++++++++++++++++++++
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.
Flask-Sockets uses the native WebSocket protocol Wrapping through gevent-websocket. Used only in modern browsers that natively support WebSockets. On the other hand, Flask-Socket IO can be used with 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 is a JavaScript SocketIO library Implements a protocol for exchanging messages. Flask-Sockets only implement channels of communication What you send is 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.
Flask-SocketIO creates an environment for event handlers that are close to the view function. This includes creating application contexts and request contexts. With some exceptions, as explained in the documentation.
++++++++++++++++++++++++++++++++++++++++++++++++++
Although the third translation is not done well I think the point is that it can be seamlessly implemented in existing Flask apps.
I was worried about the reliability of the author and the star of GitHub at the same level.
That's why I chose Flask-SocketIO. If you want to get in touch with the WebSocket protocol, or on the client (JavaScript) side Should I use Flask-Sockets if I want more degrees of freedom?
However, it seems that you can achieve what you want to do either way.
Use local Docker as in the previous article (http://qiita.com/nanakenashi/items/cbe8e8ef878121638514). My execution environment is Public Beta of Docker for Mac.
version
$ docker --version
Docker version 1.12.0, build 8eab29e, experimental
I'm just using Docker to create an environment quickly If you have an environment where new Python can run to some extent, skip the environment construction part.
flask_socket/
├ Dockerfile
└ requirements.txt
Dockerfile
#Specifying the base image
FROM python:3.5.2-alpine
#Store the directory where the source is placed as a variable
ARG project_dir=/web/socket/
#Install git after updating packages that can be installed with apk
RUN apk update
RUN apk add git
# requirements.Install the package listed in txt
WORKDIR $project_dir
ADD requirements.txt .
RUN pip install -r requirements.txt
#Flask from the GitHub repository-Get SocketIO source code
RUN git clone https://github.com/miguelgrinberg/Flask-SocketIO.git Flask-SocketIO
WORKDIR $project_dir/Flask-SocketIO/example
requirements.txt
After installing Flask
and Flask-SocketIO
with pip
$ pip freeze> requirements.txt
.
requirements.txt
click==6.6
Flask==0.11.1
Flask-SocketIO==2.6.2
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
python-engineio==0.9.2
python-socketio==1.4.4
six==1.10.0
Werkzeug==0.11.10
Except for the packages that the body of Flask
depends on
Flask-SocketIO
, python-engineio
, python-socketio
, six
have been added.
Image creation
$ cd /path/to/flask_socket/
$ docker build -t flask_socket .
-t flask_socket
: Name the image you want to create'flask_socket'.
: Use Dockerfile in the current directoryContainer startup
$ docker run -p 5000:5000 -it flask_socket /bin/sh
-p 5000: 5000
: Point host port 5000 to container port 5000-it
: Operate the container with the current input deviceflask_socket
: Specifying the image name/ bin / sh
: Execute sh command in the started containerAdd the host
setting to the application execution code.
app.py
# socketio.run(app, debug=True)
socketio.run(app, host='0.0.0.0', debug=True)
Run Flask
$ python app.py
When you access localhost: 5000
from your browser, you will see a screen like the one below.
Explain a part of the'Send:'form
Broadcast
: Send to all clients (all users who have the same page open)Disconnect
: Disconnect from the serverThe difference between ʻEcho and
Broadcast` can be easily understood by arranging the tabs.
Other items are omitted because they are interpreted as chat room related operations.
By the way, you can specify the name of the room, but you cannot enter your own name.
app.py
#Loading the required modules
from flask import Flask, render_template, session, request
from flask_socketio import SocketIO, emit, join_room, leave_room, \
close_room, rooms, disconnect
#Specifying the library to be used for asynchronous processing
# `threading`, `eventlet`, `gevent`Can be selected from
async_mode = None
#Create a Flask object and specify the session information encryption key
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
#Flask object, async_Create SocketIO server object by specifying mode
socketio = SocketIO(app, async_mode=async_mode)
#Global variable for storing threads
thread = None
app.py
#Start SocketIO Server in debug mode
socketio.run(app, debug=True)
The SocketIO library (JavaScript) is loaded in the script in html.
index.html
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
After that, specify the namespace (WebSocket communication endpoint) and A connection is made with the SocketIO server.
Specifying handlers for events issued when connecting to the server
The handler for the my response
event is also specified here.
The response seems to be added to the tag specified by the # log
selector.
index.html
namespace = '/test';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('my response', function(msg) {
$('#log').append('<br>' + $('<div/>').text('Received #' + msg.count + ': ' + msg.data).html());
});
On the server side, receive a connection request from the client with the following code
The response is returned by firing the my response
event.
The socketio.start_background_task (target = background_thread)
part will be described later.
app.py
@socketio.on('connect', namespace='/test')
def test_connect():
global thread
if thread is None:
thread = socketio.start_background_task(target=background_thread)
emit('my response', {'data': 'Connected', 'count': 0})
With the above code, the initial operation part of the Response log is displayed.
Received #0: Connected Received #1: I'm connected!
By writing a handler for the event in both Python and JavaScript I found that I could define a two-way interaction.
Echo / Broadcast has the Form tag described as follows
index.html
<form id="emit" method="POST" action='#'>
<input type="text" name="emit_data" id="emit_data" placeholder="Message">
<input type="submit" value="Echo">
</form>
<form id="broadcast" method="POST" action='#'>
<input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message">
<input type="submit" value="Broadcast">
</form>
Submitting the form executes the handler defined in the following code.
My event
event and my broadcast event
respectively
I am firing with the data entered in the form.
index.html
$('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;
});
As a result, the following code on the Python side is executed
It returns the result by firing the my response
event.
Almost the same code is lined up, but in the case of my broadcast event
By putting broadcast = True
as a keyword argument for ʻemit`
Specifies that the message should be sent to all clients.
app.py
@socketio.on('my event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my response',
{'data': message['data'], 'count': session['receive_count']})
@socketio.on('my broadcast event', namespace='/test')
def test_broadcast_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my response',
{'data': message['data'], 'count': session['receive_count']},
broadcast=True)
In the part of'Average ping / pong latency' It shows how much latency there is in communicating with the server.
The code below records the communication start time every second
I'm firing the my ping
event.
The ping_pong_times
variable is an array for storing past communications.
index.html
var ping_pong_times = [];
var start_time;
window.setInterval(function() {
start_time = (new Date).getTime();
socket.emit('my ping');
}, 1000);
The Python side only fires my pong
in response to the fire of the my ping
event.
app.py
@socketio.on('my ping', namespace='/test')
def ping_pong():
emit('my pong')
On the JavaScript side, take the difference between the time when my pong
fired and the start time
The average of past communication records is displayed in'Average ping / pong latency'.
app.py
socket.on('my pong', function() {
var latency = (new Date).getTime() - start_time;
ping_pong_times.push(latency);
ping_pong_times = ping_pong_times.slice(-30); // keep last 30 samples
var sum = 0;
for (var i = 0; i < ping_pong_times.length; i++)
sum += ping_pong_times[i];
$('#ping-pong').text(Math.round(10 * sum / ping_pong_times.length) / 10);
});
All the processing up to this point started from the client side (JavaScript). In your application, you may want to push information from the server side.
In the sample code, the handler for the connect
event had the following description:
app.py
thread = socketio.start_background_task(target=background_thread)
The background_thread
that is the target
is defined as follows.
app.py
def background_thread():
"""Example of how to send server generated events to clients."""
count = 0
while True:
socketio.sleep(10)
count += 1
socketio.emit('my response',
{'data': 'Server generated event', 'count': count},
namespace='/test')
You can see that it fires the my response
event every 10 seconds.
It seems to be useful when automatically updating the timeline or implementing an application such as Bijin Clock.
I just dropped the sample code and read it, but it was helpful. It seems that you can make various things just by modifying this a little.
Although it will be necessary to read the entire source code from now on I think Flask's appeal is that it's small, including extensions, and easy to stick to.
Recommended Posts