用于向TornadIO2服务器发送广播消息的python Socket.IO客户端

用于向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):

我正在构建一个实时web应用程序我希望能够从python应用程序的服务器端实现发送广播消息。

以下是设置:

  • 在客户端
  • 服务器作为Socket.IO服务器
  • 服务器端(框架)上的python
我可以成功地将socket.io消息从客户端发送到服务器。服务器处理这些问题并可以发送响应。下面我将描述我是如何做到这一点的

当前设置和代码 首先,我们需要定义一个处理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>
因此,调用消息和发送响应是可行的。但现在是棘手的部分

问题 响应“hello alp”仅发送给socket.io消息的调用方。我想向所有连接的客户端广播一条消息,以便在新用户加入该方时(例如在聊天应用程序中)可以实时通知他们

因此,以下是我的问题:

  • 如何向所有连接的客户端发送广播消息

  • 如何向订阅了特定频道的多个已连接客户端发送广播消息

  • 如何在python代码中的任何位置(在
    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!')