用于向TornadIO2服务器发送广播消息的python Socket.IO客户端
我正在构建一个实时web应用程序我希望能够从python应用程序的服务器端实现发送广播消息。 以下是设置:用于向TornadIO2服务器发送广播消息的python Socket.IO客户端,python,django,websocket,socket.io,tornado,Python,Django,Websocket,Socket.io,Tornado,我正在构建一个实时web应用程序我希望能够从python应用程序的服务器端实现发送广播消息。 以下是设置: 在客户端 服务器作为Socket.IO服务器 服务器端(框架)上的python 我可以成功地将socket.io消息从客户端发送到服务器。服务器处理这些问题并可以发送响应。下面我将描述我是如何做到这一点的 当前设置和代码 首先,我们需要定义一个处理socket.io事件的连接: class BaseConnection(tornadio2.SocketConnection):
- 在客户端
- 服务器作为Socket.IO服务器
- 服务器端(框架)上的python
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>
因此,调用消息和发送响应是可行的。但现在是棘手的部分
问题
响应“hello alp”仅发送给socket.io消息的调用方。我想向所有连接的客户端广播一条消息,以便在新用户加入该方时(例如在聊天应用程序中)可以实时通知他们
因此,以下是我的问题:
BaseConnection
类之外)发送广播消息?这需要某种python的Socket.IO客户机,还是内置了TornadIO2所有这些广播都应该以可靠的方式进行,所以我想WebSocket是最好的选择。但我愿意接受所有好的解决方案。我写在这里,因为在评论部分很难写。您可以在examples目录中查看Tornadio2的示例,您可以在该目录中找到聊天的实现,并且:
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)
正如您所看到的,他们将参与者实现为set()我最近在类似的设置上编写了一个非常类似的应用程序,因此我有一些见解 做你需要的事情的正确方法是有一个酒吧酒吧后台。使用simple
ConnectionHandler
s,您只能做这么多。最终,处理类级连接集开始变得丑陋(更不用说bug了)
理想情况下,您可能希望使用Redis之类的东西,将异步绑定到tornado(签出)。这样,您就不必麻烦将客户端注册到特定的频道——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()
请注意,我使用了比socket.io稳定得多的
无论如何,一旦您进行了这种设置,从任何其他客户端(如Django,在您的情况下)发送消息就像打开Redis连接(这是一种安全的选择)并发布消息一样简单:
import redis
r = redis.Redis()
r.publish('text_channel', 'oh hai!')
这个答案很长,所以我花了很大的力气写了一篇博文:如果你已经在使用django,为什么不看看gevent socketio呢 我不使用tornado,但当我在gevent上创建了具有类似功能的应用程序时。Gevent不存储连接的用户池,我将所有新的传入连接添加到列表中,并在需要时向列表中的对象发送消息。)好的,这是一种可能性。但据我所知,socket.io支持广播消息。也许有一种方法可以利用它们?这是一种幼稚的实现。任何比这更严重的事情都应该使用一个合适的pub-sub后端(见我的答案)。感谢您展示它是如何工作的。但正如Yuval Adam所指出的,使用Redis之类的东西可能是更好的选择,因为我的应用程序将相当大,需要扩展well@Alp如果你真的想创建简单的聊天应用程序,你不需要任何db后端。聊天只是我的web应用程序的一小部分,在RealTime中还有很多事情要做,我打赌这个解决方案比在Tornado中使用Redis要快。如果您使用的是无状态后端,而不是作为单个进程运行的Tornado,那么您可能希望使用Redis?这个问题:“向所有连接的客户端发送消息的函数中是否存在龙卷风”,但不是关于如何为存储连接对象创建后端的问题。@Denis,我想Yuval Adam正是回答了我想知道的问题。我还不确定100%,但是请考虑取消你的投票。Yuval Adam:你能解释一下为什么你发现Sokjs比Soko.Io更好吗?我仍然可以切换,因为我没有特定的图书馆当然。我也从socket.io开始,实际上与MrJoes(tornadio/sockjs tornado maintainer)进行了讨论。他声称sockjs具有100%的测试覆盖率,并且socket.io已知存在一些协议错误。我发现这是真的,当进入生产阶段时,sockjs确实感觉更加可靠(尽管我们确实有一些与运输无关的其他问题)。@Alp-对于“普通”应用程序,这种设置非常有效。我开发的应用程序可以发送严肃的消息(每秒数十条消息,不问;),结果Chrome真的不喜欢这些东西。Chrome在使用WebSocket 1-5分钟后就会崩溃。我试过了,但它在广播方面也有问题。我切换到sockjs+tornado+redis,它运行得非常好
import redis
r = redis.Redis()
r.publish('text_channel', 'oh hai!')