Python Socket.IO-клиент для отправки широковещательных сообщений на сервер TornadIO2
Я создаю веб-приложение в реальном времени. Я хочу иметь возможность отправлять широковещательные сообщения с серверной реализации моего приложения python.
Вот настройка:
Я могу успешно отправлять сообщения socket.io от клиента на сервер. Сервер обрабатывает их и может отправлять ответ. Ниже я опишу, как я это сделал.
Текущая настройка и код
Во-первых, нам нужно определить Connection, которое обрабатывает события socket.io:
class BaseConnection(tornadio2.SocketConnection):
def on_message(self, message):
pass
# will be run if client uses socket.emit('connect', username)
@event
def connect(self, username):
# send answer to client which will be handled by socket.on('log', function)
self.emit('log', 'hello ' + username)
Запуск сервера выполняется с помощью настраиваемого метода управления Django:
class Command(BaseCommand):
args = ''
help = 'Starts the TornadIO2 server for handling socket.io connections'
def handle(self, *args, **kwargs):
autoreload.main(self.run, args, kwargs)
def run(self, *args, **kwargs):
port = settings.SOCKETIO_PORT
router = tornadio2.TornadioRouter(BaseConnection)
application = tornado.web.Application(
router.urls,
socket_io_port = port
)
print 'Starting socket.io server on port %s' % port
server = SocketServer(application)
Хорошо, сервер работает сейчас. Добавьте код клиента:
<script type="text/javascript">
var sio = io.connect('localhost:9000');
sio.on('connect', function(data) {
console.log('connected');
sio.emit('connect', '{{ user.username }}');
});
sio.on('log', function(data) {
console.log("log: " + data);
});
</script>
Очевидно, что {{ user.username }}
будет заменено именем пользователя текущего пользователя, в этом примере имя пользователя - "alp".
Теперь, каждый раз, когда страница обновляется, вывод консоли:
connected
log: hello alp
Таким образом, вызывается обращение к сообщениям и отправке ответов. Но теперь идет сложная часть.
Проблемы
Ответ "hello alp" отправляется только в invoker сообщения socket.io. Я хочу передать сообщение всем подключенным клиентам, чтобы они могли быть проинформированы в реальном времени, если новый пользователь присоединяется к стороне (например, в приложении чата).
Итак, вот мои вопросы:
-
Как отправить широковещательное сообщение всем подключенным клиентам?
-
Как отправить широковещательное сообщение нескольким подключенным клиентам, которые подписаны на определенный канал?
-
Как я могу отправить широковещательное сообщение в любом месте моего кода python (вне класса BaseConnection
)? Будет ли это требовать своего рода Socket.IO-клиента для python или это встроено в TornadIO2?
Все эти трансляции должны быть сделаны надежным способом, поэтому я думаю, что веб-сайты - лучший выбор. Но я открыт для всех хороших решений.
Ответы
Ответ 1
Недавно я написал очень похожее приложение на аналогичной установке, поэтому у меня есть несколько идей.
Правильный способ делать то, что вам нужно, это иметь паб-вспомогательный сервер. Там просто так много можно сделать с помощью простых ConnectionHandler
s. В конце концов, обработка наборов соединений на уровне классов начинает становиться уродливой (не говоря уже о багги).
В идеале вы хотели бы использовать что-то вроде Redis, с асинхронными привязками к торнадо (посмотрите brukva). Таким образом, вам не нужно связываться с регистрацией клиентов по определенным каналам - у Redis все это из коробки.
По существу, у вас есть что-то вроде этого:
class ConnectionHandler(SockJSConnection):
def __init__(self, *args, **kwargs):
super(ConnectionHandler, self).__init__(*args, **kwargs)
self.client = brukva.Client()
self.client.connect()
self.client.subscribe('some_channel')
def on_open(self, info):
self.client.listen(self.on_chan_message)
def on_message(self, msg):
# this is a message broadcast from the client
# handle it as necessary (this implementation ignores them)
pass
def on_chan_message(self, msg):
# this is a message received from redis
# send it to the client
self.send(msg.body)
def on_close(self):
self.client.unsubscribe('text_stream')
self.client.disconnect()
Обратите внимание, что я использовал sockjs-tornado, который оказался намного более стабильным, чем socket.io.
В любом случае, если у вас есть такая настройка, отправка сообщений от любого другого клиента (например, Django, в вашем случае) так же просто, как открытие соединения Redis (redis-py - безопасная ставка) и публикация сообщения:
import redis
r = redis.Redis()
r.publish('text_channel', 'oh hai!')
Этот ответ получился довольно длинным, поэтому я прошел лишнюю милю и сделал из него сообщение в блоге: http://blog.y3xz.com/blog/2012/06/08/a-modern-python-stack-for-a-real-time-web-application/
Ответ 2
Я пишу здесь, потому что это трудно написать в разделе комментариев. Вы можете просмотреть примеры для tornadoio2 в каталоге примеров, где вы можете найти реализацию чата, и:
class ChatConnection(tornadio2.conn.SocketConnection):
# Class level variable
participants = set()
def on_open(self, info):
self.send("Welcome from the server.")
self.participants.add(self)
def on_message(self, message):
# Pong message back
for p in self.participants:
p.send(message)
Как вы видите, они реализовали участников как заданные))
Ответ 3
Если вы уже используете django, почему бы не взглянуть на gevent-socketio.