Простое руководство пользователя Flask-SocketIO

Python сервер Flask Gevent WebSocket

Добро пожаловать в мой публичный аккаунт WeChatAlwaysBeta, вас ждет еще больше захватывающего контента.

Flask-SocketIOПозволяет приложениям Flask получать доступ к двусторонней связи с малой задержкой между клиентом и сервером. Клиентские приложения могут использовать любой из Javascript, C++, Java и Swift.SocketIOОфициальная клиентская библиотека или любой совместимый клиент для установления постоянного подключения к серверу.

Установить

Установить напрямую с помощью pip:

pip install flask-socketio

Требовать

Flask-SocketIO совместим с Python 2.7 и Python 3.3+. Асинхронные службы, от которых зависит этот пакет, можно выбрать из трех вариантов:

  • eventletЛучшая производительность, поддержка длительного опроса и транспорта WebSocket.
  • geventПоддерживается во многих различных конфигурациях. Пакет gevent полностью поддерживает транспорты с длительным опросом, но, в отличие от eventlet, gevent не имеет встроенной поддержки WebSocket. Чтобы добавить поддержку WebSockets, в настоящее время есть два варианта: установитьgevent-websocketпакет добавляет поддержку WebSocket в gevent, или вы можете использовать пакет с поддержкой WebSocketuWSGIВеб сервер. Использование gevent также является высокопроизводительным вариантом, но немного ниже, чем eventlet.
  • Также можно использовать сервер разработки Flask на основе Werkzeug, с той оговоркой, что ему не хватает производительности по сравнению с двумя другими вариантами, поэтому его следует использовать только для простых сред разработки. Эта опция поддерживает только транспорты с длительным опросом.

Расширение автоматически определяет, какую асинхронную платформу использовать, в зависимости от того, что установлено. Установите приоритет eventlet, затем gevent. Для поддержки WebSocket в gevent предпочтительнее uWSGI, чем gevent-websocket. Если ни eventlet, ни gevent не установлены, используется сервер разработки Flask.

Если используется несколько процессов, процессы используют службу очереди сообщений для координации таких операций, как широковещательная рассылка. Поддерживаемые очередиRedis,RabbitMQа такжеKombuЛюбая другая очередь сообщений, поддерживаемая пакетом.

На стороне клиента для установления соединения с сервером можно использовать официальную клиентскую библиотеку Socket.IO Javascript. Также есть официальные клиенты, написанные на Swift, Java и C++. Неофициальные клиенты тоже работают, пока реализуютПротокол Socket.IO.

инициализация

В следующем примере кода показано, как добавить Flask-SocketIO в приложение Flask:

from flask import Flask, render_template
from flask_socketio import SocketIO

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

if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', debug=True)

Приведенный выше код завершает создание простого веб-сервера.

socketio.run()Функция инкапсулирует запуск веб-сервера и заменяетapp.run()Запустится стандартный сервер разработки Flask.

Сервер разработки Werkzeug по-прежнему используется внутри компании и правильно настроен, когда приложение находится в режиме отладки.socketio.run().

В производственном режиме используется веб-сервер eventlet, если он доступен, в противном случае используется веб-сервер gevent. Если eventlet и gevent не установлены, веб-сервер разрабатывается с помощью Werkzeug.

Интерфейс командной строки, основанный на кликах, представленный во Flask 0.11. Это расширение предоставляет новую версию команды для запуска сервера Socket.IO. Пример использования:flask run

$ FLASK_APP=my_app.py flask run

Или напрямую используйте следующие методы для запуска проекта:

$ python2.7 app.py

событие соединения

Flask-SocketIO отправляет события подключения и отключения. В следующем примере показано, как зарегистрировать для них обработчики:

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

@socketio.on('disconnect', namespace='/test')
def test_disconnect():
    print('Client disconnected')

Обработчики событий подключения могут дополнительно возвращатьFalseчтобы отказаться от соединения. Это позволяет клиенту быть аутентифицированным в этот момент.

Обратите внимание, что события подключения и отключения будут отправляться отдельно для каждого используемого пространства имен.

получить сообщение

При использовании SocketIO обе стороны получают сообщения как события. Используйте обратные вызовы Javascript на стороне клиента. При использовании Flask-SocketIO серверу необходимо зарегистрировать обработчики этих событий, подобно тому, как функции представления обрабатывают маршрутизацию.

В следующем примере создается обработчик событий на стороне сервера для безымянного события:

@socketio.on('message')
def handle_message(message):
    print('received message: ' + message)

В приведенном выше примере используется строковое сообщение. Другой тип безымянного события использует данные JSON:

@socketio.on('json')
def handle_json(json):
    print('received json: ' + str(json))

Самый гибкий способ — использовать собственное имя события, и это наиболее распространенный способ во время разработки.

Данные сообщения события могут быть строками, байтами, целыми числами или JSON:

@socketio.on('my event')
def handle_my_custom_event(json):
    print('received json: ' + str(json))

Пользовательские именованные события также могут поддерживать несколько параметров:

@socketio.on('my event')
def handle_my_custom_event(arg1, arg2, arg3):
    print('received args: ' + arg1 + arg2 + arg3)

Flask-SocketIO поддерживает пространство имен SocketIO, позволяя клиентам мультиплексировать несколько независимых подключений в один и тот же физический сокет:

@socketio.on('my event', namespace='/test')
def handle_my_custom_namespace_event(json):
    print('received json: ' + str(json))

Если пространство имен не указано,'/'то используется глобальное пространство имен по умолчанию с именем.

В случаях, когда синтаксис декоратора неудобен,on_eventВы можете использовать этот метод:

def my_function_handler(data):
    pass

socketio.on_event('my event', my_function_handler, namespace='/test')

Клиенты могут запросить обратный вызов с подтверждением, подтверждая получение отправленного ими сообщения. Любое значение, возвращаемое функцией-обработчиком, будет передано клиенту в качестве параметра в функции обратного вызова:

@socketio.on('my event')
def handle_my_custom_event(json):
    print('received json: ' + str(json))
    return 'one', 2

В приведенном выше примере функция обратного вызова клиента будет вызываться с двумя параметрами:'one'и2. Если функция-обработчик не возвращает значения, клиентская функция обратного вызова будет вызываться без аргументов.

Отправить сообщение

Обработчики событий SocketIO, определенные, как показано в предыдущем разделе, могут использоватьsend()иemit()Функция отправляет ответное сообщение подключенному клиенту.

В следующем примере полученные события возвращаются клиенту, который их отправил:

from flask_socketio import send, emit

@socketio.on('message')
def handle_message(message):
    send(message)

@socketio.on('json')
def handle_json(json):
    send(json, json=True)

@socketio.on('my event')
def handle_my_custom_event(json):
    emit('my response', json)

Обратите внимание, какsend()иemit()Для безымянных и именованных событий соответственно.

При работе с пространствами именsend()иemit()По умолчанию для использования пространства имен входящих сообщений. по желаниюnamespaceПараметр указывает разные пространства имен:

@socketio.on('message')
def handle_message(message):
    send(message, namespace='/chat')

@socketio.on('my event')
def handle_my_custom_event(json):
    emit('my response', json, namespace='/chat')

Чтобы отправить событие с несколькими параметрами, отправьте кортеж:

@socketio.on('my event')
def handle_my_custom_event(json):
    emit('my response', ('foo', 'bar', json), namespace='/chat')

SocketIO поддерживает обратные вызовы подтверждения, чтобы подтвердить, что клиент получил сообщение:

def ack():
    print 'message was received!'

@socketio.on('my event')
def handle_my_custom_event(json):
    emit('my response', json, callback=ack)

При использовании обратных вызовов клиент Javascript получает функцию обратного вызова для вызова при получении сообщения. После того как клиентское приложение вызывает функцию обратного вызова, сервер вызывает соответствующий обратный вызов на стороне сервера. Если обратные вызовы на стороне клиента вызываются с аргументами, эти обратные вызовы также предоставляются в качестве аргументов для обратных вызовов на стороне сервера.

транслировать

Еще одна очень полезная функция SocketIO — широковещательная рассылка сообщений. SocketIO поддерживается этой функциейbroadcast=Trueнеобязательный параметрsend()иemit():

@socketio.on('my event')
def handle_my_custom_event(data):
    emit('my response', data, broadcast=True)

Когда сообщение отправляется с включенной опцией широковещательной рассылки, оно принимается всеми клиентами, подключенными к пространству имен, включая отправителя. Клиенты, подключающиеся к глобальному пространству имен, получат это сообщение, если пространство имен не используется. Обратите внимание, что обратные вызовы не вызываются для широковещательных сообщений.

Во всех показанных здесь примерах сервер отвечает на события, отправленные клиентом. Но для некоторых приложений сервер должен быть отправителем сообщения. Это полезно для отправки уведомлений клиентам о событиях на сервере (например, в фоновом потоке).socketio.send()иsocketio.emit()можно использовать для трансляции всем подключенным клиентам:

def some_function():
    socketio.emit('some event', {'data': 42})

Пожалуйста, обрати внимание,socketio.send()иsocketio.emit()понимания в контексте иsend()иemit()функция другая. Также обратите внимание, что в приведенном выше использовании нет клиентского контекста, поэтомуbroadcast=Trueиспользуется по умолчанию и не требует указания.

Номер

Для многих приложений необходимо сгруппировать пользователей в подмножества, к которым можно обращаться вместе. Лучшим примером является чат-приложение с несколькими комнатами, где пользователи получают сообщения из той комнаты, в которой они находятся, но не из других комнат, где находятся другие пользователи. SocketIO поддерживает концепцию проходных комнатjoin_room()иleave_room()Функции:

from flask_socketio import join_room, leave_room

@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    send(username + ' has entered the room.', room=room)

@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    send(username + ' has left the room.', room=room)

существуетsend()иemit()функция принимает необязательныйroomОператор, который вызывает отправку сообщения всем клиентам, которые находятся в бронировании.

Всем клиентам при подключении назначается комната, названная в честь идентификатора подключенного сеанса, из которой они могут получитьrequest.sid. Данный клиент может присоединиться к любой комнате и ему может быть присвоено любое имя. Когда клиент отключается, он будет удален из всех комнат, в которых он находится. нет контекстаsocketio.send()иsocketio.emit()Функция также принимаетroomпараметр для трансляции всем клиентам в комнате.

Поскольку всем клиентам назначаются отдельные комнаты, для отправки сообщений одному клиенту в качестве параметра комнаты можно использовать идентификатор сеанса клиента.

обработка ошибок

Flask-SocketIO также может обрабатывать исключения:

@socketio.on_error()        # Handles the default namespace
def error_handler(e):
    pass

@socketio.on_error('/chat') # handles the '/chat' namespace
def error_handler_chat(e):
    pass

@socketio.on_error_default  # handles all namespaces without an explicit error handler
def default_error_handler(e):
    pass

Функция обработки ошибок принимает объект исключения в качестве параметра.

также можно использоватьrequest.eventПеременная проверяет параметры сообщения и данных текущего запроса, что полезно для регистрации ошибок и отладки вне обработчиков событий:

from flask import request

@socketio.on("my error event")
def on_my_event(data):
    raise RuntimeError()

@socketio.on_error_default
def default_error_handler(e):
    print(request.event["message"]) # "my error event"
    print(request.event["args"])    # (data,)

пространство имен на основе классов

В качестве альтернативы обработчикам событий на основе декораторов, описанным выше, обработчики событий, принадлежащие пространству имен, могут быть созданы как методы класса.flask_socketio.NamespaceПредоставляется в качестве базового класса для создания пространств имен на основе классов:

from flask_socketio import Namespace, emit

class MyCustomNamespace(Namespace):
    def on_connect(self):
        pass

    def on_disconnect(self):
        pass

    def on_my_event(self, data):
        emit('my_response', data)

socketio.on_namespace(MyCustomNamespace('/test'))

При использовании пространств имен на основе классов любые события, полученные сервером, будут отправлены вon_Метод префикса имени события. Например, событиеmy_eventбудет обрабатываться методом с именемon_my_event. Если полученное событие не имеет соответствующего метода, определенного в классе пространства имен, событие игнорируется. Все имена событий, используемые в пространствах имен на основе классов, должны использовать символы, допустимые в именах методов.

Чтобы упростить методы, определенные в пространствах имен на основе классов, экземпляры пространств имен включают версии нескольких методов в классе,flask_socketio.SocketIOкогдаnamespaceЕсли аргументы не указаны, эти методы по умолчанию используют правильное пространство имен.

Если у события есть обработчик в пространстве имен на основе класса, а также обработчик функции на основе декоратора, вызывается только декорированный обработчик функции.

контрольная работа

Выше приведен перевод документа с официального сайта, поговорим о том, как проводить отладку после написания кода.

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
    var socket = io.connect('http://' + document.domain + ':' + location.port);
    socket.on('connect', function() {
        socket.emit('my event', {data: 'I\'m connected!'});
    });
</script>

Используйте JavaScript для подключения к серверу, вот проблема, с которой я столкнулся, первое использованиеjsbinПришел протестировать, но подключиться к бэкенду не получилось, причина в том, что jsbin - это HTTPS, а мой запрос - HTTP, поэтому я честно написал HTML-файл, исходники могут быть прямо вGithubскачать.

<!DOCTYPE HTML>
<html>
<head>
    <title>Flask-SocketIO Test</title>
    <script type="text/javascript" src="//code.jquery.com/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {
            namespace = '/test';
            var socket = io.connect(location.protocol + '//' + 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());
            });

            $('form#emit').submit(function(event) {
                socket.emit('my_event', {data: $('#emit_data').val()});
                return false;
            });
        });
    </script>
</head>
<body>
    <h1>Flask-SocketIO Test</h1>
    <p>Async mode is: <b>{{ async_mode }}</b></p>
    <h2>Send:</h2>
    <form id="emit" method="POST" action='#'>
        <input type="text" name="emit_data" id="emit_data" placeholder="Message">
        <input type="submit" value="Echo">
    </form>
    <h2>Receive:</h2>
    <div id="log"></div>
</body>
</html>

Если у вас есть эта страница, вы можете ввести прямо в браузереhttp://127.0.0.1:5000Доступ к серверу, дополнительные функции могут быть выбраны по желанию.



Связанные документы:

GitHub.com/Miguel Next Day NB…

flask-socketio.readthedocs.io/en/latest/

windrocblog.sinaapp.com/?p=1628

zhuanlan.zhihu.com/p/31118736

Paradise.club/2016/04/10/…