【问题标题】:Autobahn sending user specific and broadcast messages from external application高速公路从外部应用程序发送用户特定和广播消息
【发布时间】:2015-07-09 05:02:50
【问题描述】:

对 websockets 完全陌生。

我在理解如何与另一个应用程序中的 python Autobahn/twisted 交互时遇到了一些麻烦,并且似乎找不到任何有用的示例。

我有一个 Python 应用程序正在运行,它需要在某些事件上发送两种类型的消息之一。第一个是对所有用户的广播消息。第二种类型是针对单个特定用户的。

使用以下两个示例,我可以接收消息并发送响应。但是,我不需要从连接的客户端(连接到 websockets 服务器的客户端除外)接收任何内容,只发送给他们。

我玩过:https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/echo

另外(非高速公路相关):https://github.com/opiate/SimpleWebSocketServer

问题:

1 - 我正在尝试做的事情可能吗?我能否拥有一个与 Autobahn 应用程序/服务器连接并向所有连接用户或单个用户广播消息的外部应用程序。

2 - 如果可能的话,有人可以指出正确的方向来了解如何做到这一点吗?

谢谢

【问题讨论】:

    标签: python websocket twisted autobahn


    【解决方案1】:

    首先,Autobahn 项目提供了通信协议 WAMP 的开源实现。 WAMP 提供了两种通信模式 RPC (Remote-Procedure-Call) 和 PUBSUB (Publish-Subscribe)。因此,在您的情况下,有必要弄清楚这两种模式中的哪一种适合您的需求。

    RPC

    根据WAMP FAQ RPC,RPC 涉及三个角色。它们是:

    • 来电者
    • 被调用者
    • 经销商

    在您的情况下,被调用者是服务器,而调用者(客户端)调用服务器上的方法。这显然对你有用。 (可以将返回值发送到被调用方/客户端)。经销商负责路由,目前可以忽略。因此,考虑到上述模式,它似乎不适合您的问题。

    PUBSUB

    第二种模式是PUBSUB。该模式由三个角色组成(取自WAMP FAQ PUBSUB):

    • 出版商
    • 订阅者
    • 经纪人

    因此,发布者(服务器)将事件发布到主题。订阅者(客户端)可以订阅发布者的主题。发布事件后,订阅者会收到包含有效负载的事件。这意味着您可以提供一个主题“广播”并让所有客户订阅该主题。如果需要,您可以向所有客户端发送广播消息。

    然后您必须处理向单个客户端(订阅者)发送消息的问题。根据文档,发布主题的发布函数有一个可选参数,用于提供有资格接收事件的“客户端”列表。 WAMP Documentation (Class Publish)

    --------编辑--------

    目前尚不清楚“外部应用程序”是什么意思,以及应该用什么语言编写。如果外部应用程序是用 python、JavaScript 或 Cpp 编写的,或者是使用带有 (WAMP) 的 Autobahn 框架的 Android 应用程序,则可以解决作者解释的问题。

    如问题中所述,Autobahn 还提供 websocket 协议实现。解决问题的另一种方法是使用 Autobahn Websockets,对于“外部应用程序”,选择 Websocket 实现。 Autobahn 提供 Python 和 Android Websocket 解决方案。当然还有更多可用的 Websocket 库或模块。 Java Websocket libraryPython Websocket Client module 等等……

    假设 Websocket 服务器是使用 Autobahn 框架实现的。外部应用程序是另一个连接到服务器并发送以“send_broadcast:PAYLOAD”开头的定义字符串和附加的有效负载的客户端。在服务器上,您可以检查消息中的字符串,如果消息以“send_broadcast”开头,您可以将广播发送到所有连接的客户端。如果您只想将消息发送给一个客户端,您可以定义另一个字符串,例如“send_to_single:IP:PAYLOAD”。然后,服务器实现可以有另一个 elif 分支来检查“send_to_single”并调用另一个方法,也许是“def send_to_single”?并在给定客户端 ip 的情况下传递另一个参数。您可以仅将 msg 发送给给定的客户端,而不是像在广播方法中那样发送给所有客户端。您自己的通信协议的另一种方式是使用 JSON。 您可以按如下方式定义您的味精:

    {
        "type": "broadcast",
        "msg": "your_message"
    }
    

    {
        "type": "single",
        "elegible": ["IP_1", "???"],
        "msg": "your_message"
    }
    

    然后在服务器上加载有效负载,检查类型并执行进一步的步骤。

    服务器

    import sys
    
    from twisted.internet import reactor
    from twisted.python import log
    from twisted.web.server import Site
    from twisted.web.static import File
    
    from autobahn.twisted.websocket import WebSocketServerFactory, \
        WebSocketServerProtocol, \
        listenWS
    
    
    class BroadcastServerProtocol(WebSocketServerProtocol):
    
        def onOpen(self):
            self.factory.register(self)
    
        def onConnect(self, request):
            print("Client connecting: {}".format(request.peer))
    
        def onMessage(self, payload, isBinary):
            if not isBinary:
                if "send_broadcast" in payload.decode('utf8'):
                    msg = "Send broadcast was ordered"
                    self.factory.broadcast(msg)
    
        def connectionLost(self, reason):
            WebSocketServerProtocol.connectionLost(self, reason)
            self.factory.unregister(self)
    
    
    class BroadcastServerFactory(WebSocketServerFactory):
    
        """
        Simple broadcast server broadcasting any message it receives to all
        currently connected clients.
        """
    
        def __init__(self, url, debug=False, debugCodePaths=False):
            WebSocketServerFactory.__init__(self, url, debug=debug, debugCodePaths=debugCodePaths)
            self.clients = []
            self.tickcount = 0
            self.tick()
    
        def tick(self):
            self.tickcount += 1
            self.broadcast("tick %d from server" % self.tickcount)
            reactor.callLater(1, self.tick)
    
        def register(self, client):
            if client not in self.clients:
                print("registered client {}".format(client.peer))
                self.clients.append(client)
    
        def unregister(self, client):
            if client in self.clients:
                print("unregistered client {}".format(client.peer))
                self.clients.remove(client)
    
        def broadcast(self, msg):
            print("broadcasting message '{}' ..".format(msg))
            for c in self.clients:
                c.sendMessage(msg.encode('utf8'))
                print("message sent to {}".format(c.peer))
    
    
    class BroadcastPreparedServerFactory(BroadcastServerFactory):
    
        """
        Functionally same as above, but optimized broadcast using
        prepareMessage and sendPreparedMessage.
        """
    
        def broadcast(self, msg):
            print("broadcasting prepared message '{}' ..".format(msg))
            preparedMsg = self.prepareMessage(msg)
            for c in self.clients:
                c.sendPreparedMessage(preparedMsg)
                print("prepared message sent to {}".format(c.peer))
    
    
    if __name__ == '__main__':
    
        if len(sys.argv) > 1 and sys.argv[1] == 'debug':
            log.startLogging(sys.stdout)
            debug = True
        else:
            debug = False
    
        ServerFactory = BroadcastServerFactory
        # ServerFactory = BroadcastPreparedServerFactory
    
        factory = ServerFactory("ws://localhost:9000",
                                debug=debug,
                                debugCodePaths=debug)
    
        factory.protocol = BroadcastServerProtocol
        factory.setProtocolOptions(allowHixie76=True)
        listenWS(factory)
    
        webdir = File(".")
        web = Site(webdir)
        reactor.listenTCP(8080, web)
    
        reactor.run()
    

    客户 客户端也是用 Python 编写的,使用 不同的 模块实现,并且仍然可以工作。使用 Websocket 协议与 Websocket 服务器通信当然是必要的。

    from websocket import create_connection
    ws = create_connection("ws://localhost:9000")
    print "Sending 'send_broadcast'..."
    ws.send("send_broadcast:PAYLOAD")
    print "Sent"
    print "Reeiving..."  # OPTIONAL
    result = ws.recv()   # OPTIONAL
    print "Received '%s'" % result    # OPTIONAL
    ws.close(
    

    )

    【讨论】:

    • 这个问题还没有回答。上面的示例不广播来自外部源的消息,它们只是使用睡眠每 5 秒广播一条内部消息。
    • 所以请解释一下您的外部来源是什么。智能手机(IOS/Android)还是浏览器?还是电脑/笔记本电脑?
    • 好吧,我编辑了我的答案,提示如何连接到服务器并要求服务器向单个客户端发送广播或消息。它不包括单个客户端消息,但解释了如何管理它。问题是指出正确的方向。我希望答案有助于了解从这里去哪里。
    猜你喜欢
    • 2015-05-02
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-11
    • 2013-10-04
    • 1970-01-01
    相关资源
    最近更新 更多