高速公路从外部应用程序发送用户特定和广播消息

Posted

技术标签:

【中文标题】高速公路从外部应用程序发送用户特定和广播消息【英文标题】: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 - 如果可能的话,有人可以指出正确的方向来了解如何做到这一点吗?

谢谢

【问题讨论】:

【参考方案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 library、Python 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)还是浏览器?还是电脑/笔记本电脑? 好吧,我编辑了我的答案,提示如何连接到服务器并要求服务器向单个客户端发送广播或消息。它不包括单个客户端消息,但解释了如何管理它。问题是指出正确的方向。我希望答案有助于了解从这里去哪里。

以上是关于高速公路从外部应用程序发送用户特定和广播消息的主要内容,如果未能解决你的问题,请参考以下文章

在初始广播消息后向特定 Smack 域发送消息

将广播从一个进程发送到另一个进程

私人广播发送者和接收者权限

Laravel Websocket 向特定的 Android 设备发送消息

BroadcastReceiver机制-Android12

BroadcastReceiver机制-Android12