[PYTHON] WebSocket-Anwendung mit Flask-Socket IO

Einführung

Weil es aus Versehen notwendig wurde, eine Chat-Anwendung zu implementieren Ich habe untersucht, wie ich eine WebSocket-Anwendung mit Flask implementieren kann.

Ich werde bis zu dem Punkt schreiben, an dem ich versuche, die Probe zu verschieben Dies allein reicht aus, um die Hauptfunktionen des Chats zu nutzen.

Erweiterungsauswahl

Ich werde es auf Flask 'Schulter implementieren Die folgenden zwei Erweiterungen wurden als Optionen aufgeführt.

Es ist ein wenig alt wie im Februar 2014, aber im Erklärenden Artikel von miguelgrinberg Es gibt einen Abschnitt, der den Vergleich zwischen den beiden erklärt.

++++++++++++++++++++++++++++++++++++++++++++++++++

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 verwendet das native WebSocket-Protokoll Durch gevent-Websocket wickeln. Wird nur in modernen Browsern verwendet, die WebSocket nativ unterstützen. Auf der anderen Seite kann Flask-Socket IO mit älteren Browsern verwendet werden.

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 ist eine JavaScript SocketIO-Bibliothek Es implementiert ein Protokoll zum Austausch von Nachrichten. Flask-Sockets implementieren nur Kommunikationskanäle Was Sie senden, hängt von der Bewerbung ab.

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 erstellt eine Umgebung für Ereignishandler, die sich in der Nähe von Ansichtsfunktionen befinden. Dies umfasst das Generieren von Anwendungskontexten und Anforderungskontexten. Mit einigen Ausnahmen, wie in der Dokumentation erläutert.

++++++++++++++++++++++++++++++++++++++++++++++++++

Obwohl der dritte Punkt nicht gut übersetzt wurde Ich denke, der Punkt ist, dass es nahtlos in bestehende Flask-Apps implementiert werden kann.

Ich war besorgt über die Zuverlässigkeit des Autors und des Stars von GitHub auf dem gleichen Niveau.

Deshalb habe ich [Flask-SocketIO] gewählt (https://github.com/miguelgrinberg/Flask-SocketIO). Wenn Sie mit dem WebSocket-Protokoll oder auf der Client-Seite (JavaScript) in Kontakt treten möchten Soll ich Flask-Sockets für die Freiheit verwenden?

Es scheint jedoch, dass Sie in beiden Fällen das erreichen können, was Sie tun möchten.

Vorbereitung

Verwenden Sie den lokalen Docker wie im vorherigen Artikel (http://qiita.com/nanakenashi/items/cbe8e8ef878121638514). Meine Ausführungsumgebung ist Public Beta von Docker für Mac.

Ausführung


$ docker --version
Docker version 1.12.0, build 8eab29e, experimental

Ich verwende Docker nur, um schnell eine Umgebung zu erstellen Wenn Sie eine Umgebung haben, in der neues Python bis zu einem gewissen Grad ausgeführt werden kann, überspringen Sie den Umgebungskonstruktionsteil.

Quelle

flask_socket/
    ├ Dockerfile
    └ requirements.txt

Dockerfile

#Angeben des Basisbildes
FROM python:3.5.2-alpine

#Speichern Sie das Verzeichnis, in dem sich die Quelle befindet, als Variable
ARG project_dir=/web/socket/

#Installiere git nach dem Aktualisieren von Paketen, die mit apk installiert werden können
RUN apk update
RUN apk add git

# requirements.Installieren Sie das in txt aufgeführte Paket
WORKDIR $project_dir
ADD requirements.txt .
RUN pip install -r requirements.txt

#Flasche aus dem GitHub-Repository-Holen Sie sich den SocketIO-Quellcode
RUN git clone https://github.com/miguelgrinberg/Flask-SocketIO.git Flask-SocketIO
WORKDIR $project_dir/Flask-SocketIO/example

requirements.txt

Nach der Installation von Flask und Flask-Socket IO mit Pip $ pip freeze> require.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

Mit Ausnahme der Pakete, von denen der Körper von "Flask" abhängt Flask-SocketIO, python-engineio, python-socketio, six wurden hinzugefügt.

Verfahren

Bilderzeugung

Bilderzeugung


$ cd /path/to/flask_socket/
$ docker build -t flask_socket . 

Container-Start

Container-Start


$ docker run -p 5000:5000 -it flask_socket /bin/sh

Code hinzufügen

Fügen Sie dem Anwendungsausführungscode die Einstellung "Host" hinzu.

app.py


# socketio.run(app, debug=True)
socketio.run(app, host='0.0.0.0', debug=True)

Führen Sie die Beispielanwendung aus

Flaschenausführung


$ python app.py

Wenn Sie über Ihren Browser auf localhost: 5000 zugreifen, wird ein Bildschirm wie der folgende angezeigt.

Flask-SocketIO.png

Erläutern Sie einen Teil des Formulars "Senden:"

Der Unterschied zwischen "Echo" und "Broadcast" kann leicht durch Anordnen der Registerkarten verstanden werden. Andere Elemente werden weggelassen, da sie als Chatroom-bezogene Vorgänge interpretiert werden. Sie können übrigens den Namen des Raums angeben, aber keinen eigenen Namen eingeben.

Code-Interpretation

Verarbeitung bei der Anwendungsausführung

app.py


#Laden der benötigten Module
from flask import Flask, render_template, session, request         
from flask_socketio import SocketIO, emit, join_room, leave_room, \
      close_room, rooms, disconnect                                  

#Angabe der Bibliothek, die für die asynchrone Verarbeitung verwendet werden soll
# `threading`, `eventlet`, `gevent`Kann ausgewählt werden aus
async_mode = None

#Erstellen Sie ein Flask-Objekt und geben Sie den Schlüssel für die Verschlüsselung der Sitzungsinformationen an
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'

#Flaschenobjekt, asynchron_Erstellen Sie ein SocketIO-Serverobjekt, indem Sie den Modus angeben
socketio = SocketIO(app, async_mode=async_mode)

#Globale Variable zum Speichern von Threads
thread = None

app.py


#Starten Sie den SocketIO-Server im Debug-Modus
socketio.run(app, debug=True)

Was tun, wenn die Seite geöffnet wird?

Die SocketIO-Bibliothek (JavaScript) wird in das Skript in HTML geladen.

index.html


<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>

Geben Sie danach den Namespace (Endpunkt der WebSocket-Kommunikation) und an Eine Verbindung wird mit dem SocketIO-Server hergestellt.

Angeben von Handlern für Ereignisse, die beim Herstellen einer Verbindung zum Server ausgegeben werden Hier wird auch der Handler für das Ereignis "Meine Antwort" angegeben. Die Antwort scheint zu dem Tag hinzugefügt zu werden, das durch den Selektor # log angegeben wird.

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());
});                                                                                                                                                   

Empfangen Sie auf der Serverseite eine Verbindungsanforderung vom Client mit dem folgenden Code Die Antwort wird zurückgegeben, indem das Ereignis "Meine Antwort" ausgelöst wird. Der Teil von "socketio.start_background_task (target = background_thread)" wird später beschrieben.

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})

Mit dem obigen Code wird der erste Operationsteil des Antwortprotokolls angezeigt.

Received #0: Connected Received #1: I'm connected!

Durch Schreiben eines Handlers für das Ereignis in Python und JavaScript Ich fand heraus, dass ich eine wechselseitige Interaktion definieren konnte.

Verarbeitung während Echo / Broadcast

Echo / Broadcast hat das Formular-Tag wie folgt beschrieben

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>

Durch das Senden des Formulars wird der im folgenden Code definierte Handler ausgeführt. My Event Event bzw. My Broadcast Event Ich feuere mit den im Formular eingegebenen Daten.

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;
});

Infolgedessen wird der folgende Code auf der Python-Seite ausgeführt Rückgabe des Ergebnisses durch Auslösen des Ereignisses "Meine Antwort". Fast der gleiche Code ist in einer Reihe, aber im Fall von "Mein Sendeereignis" Indem Sie "Broadcast = True" als Schlüsselwortargument für "emit" eingeben Gibt an, dass die Nachricht an alle Clients gesendet werden soll.

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)

Ping / Pong-Verarbeitung

Im Teil "Durchschnittliche Ping / Pong-Latenz" Es zeigt, wie viel Latenz bei der Kommunikation mit dem Server besteht.

Der folgende Code zeichnet die Kommunikationsstartzeit jede Sekunde auf Ich feuere das "Mein Ping" -Event ab. Die Variable "ping_pong_times" ist ein Array zum Speichern vergangener Kommunikationen.

index.html


var ping_pong_times = [];
var start_time;
window.setInterval(function() {
    start_time = (new Date).getTime();
    socket.emit('my ping');
}, 1000);

Die Python-Seite feuert "mein Pong" nur als Reaktion auf das Feuer des "mein Ping" -Ereignisses ab.

app.py


@socketio.on('my ping', namespace='/test')
def ping_pong():
    emit('my pong')

Nehmen Sie auf der JavaScript-Seite den Unterschied zwischen der Zeit, zu der "mein Pong" abgefeuert wurde, und der Startzeit Der Durchschnitt vergangener Kommunikationsaufzeichnungen wird unter "Durchschnittliche Ping- / Pong-Latenz" angezeigt.

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);
});

Ereignisgenerierungsverarbeitung auf der Serverseite

Die gesamte Verarbeitung bis zu diesem Punkt begann auf der Clientseite (JavaScript). In Ihrer Anwendung möchten Sie möglicherweise Informationen von der Serverseite übertragen.

Im Beispielcode hatte der Handler für das Ereignis "connect" die folgende Beschreibung:

app.py


thread = socketio.start_background_task(target=background_thread)

Der "Hintergrund_Thread", der "Ziel" ist, ist wie folgt definiert.

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')

Sie können sehen, dass alle 10 Sekunden das Ereignis "Meine Antwort" ausgelöst wird. Dies scheint nützlich zu sein, wenn die Zeitleiste automatisch aktualisiert oder eine Anwendung wie Bijin Clock implementiert wird.

Fazit

Ich habe gerade den Beispielcode gelöscht und gelesen, aber er war hilfreich. Es scheint, dass Sie verschiedene Dinge machen können, indem Sie dies ein wenig ändern.

Obwohl es von nun an notwendig sein wird, den gesamten Quellcode zu lesen Ich denke, Flask hat den Reiz, dass es klein ist, auch wenn es Erweiterungen enthält, und dass es leicht zu halten ist.

Recommended Posts

WebSocket-Anwendung mit Flask-Socket IO
WebSocket mit Python + uWSGI
Entwicklung von Webanwendungen mit Flask
Erstellen Sie eine Webanwendung mit Django
Laden Sie den Test-Websocket mit Locust
Webanwendung mit Python + Flask ② ③
Erstellen Sie eine Rails-Anwendung mit Docker
Webanwendung mit Python + Flask ④
Messen Sie die Abdeckung der Django-Anwendung mit Coverage.py
Stellen Sie die Django-Anwendung mit Docker bereit
Überwachen Sie die Leistung von Python-Anwendungen mit Dynatrace ♪
Twitter-Posting-Anwendung mit Django gemacht
Erstellen Sie eine Webanwendung mit Django
Anwendungsentwicklung mit Docker + Python + Flask
Anwendung von Python: Datenbereinigung Teil 2: Datenbereinigung mit DataFrame
Plotten Anwenden von Diagrammen mit Schiebereglern
Automatisieren Sie Windows-Anwendungstests mit Windows Application Driver-Python Edition