高速公路从外部应用程序发送特定于用户的消息和广播消息 [英] Autobahn sending user specific and broadcast messages from external application

查看:23
本文介绍了高速公路从外部应用程序发送特定于用户的消息和广播消息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对 websockets 完全陌生.

我在理解如何与 python Autobahn/twisted from another application 交互时遇到了一些麻烦,似乎找不到任何有用的示例.

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

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

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

此外(与高速公路无关):https://github.com/opiate/SimpleWebSocketServer

问题:

1 - 我正在尝试做的可能吗?我可以有一个外部应用程序与高速公路应用程序/服务器连接并向所有连接的用户或单个用户广播消息.

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

谢谢

解决方案

首先,高速公路项目提供了通信协议 WAMP 的开源实现.WAMP 提供两种通信模式RPC(远程程序调用)和PUBSUB(发布-订阅).因此,在您的情况下,有必要弄清楚这两种模式中的哪一种适合您的需求.

RPC

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

  • 来电者
  • 被叫方
  • 经销商

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

发布订阅

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

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

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

那么你就必须处理向单个客户端(订阅者)发送消息的问题.根据文档,发布主题的发布函数有一个可选参数,用于提供有资格接收事件的客户端"列表.WAMP 文档(类发布)

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

不清楚外部应用程序"是什么意思以及它应该用什么语言编写.如果外部应用程序是用 python、JavaScript 或 Cpp 编写的,或者使用 Autobahn 框架和 (WAMP) 编写的 Android 应用程序,则可以解决作者解释的问题.

如问题中所述,Autobahn 还提供了一个 websocket 协议实现.解决该问题的另一种方法是使用高速公路 Websockets 并为外部应用程序"选择 Websocket 实现.Autobahn 提供 Python 和 Android Websocket 解决方案.当然,还有更多可用的 Websocket 库或模块.Java Websocket 库Python Websocket 客户端模块 和更多...

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

<代码>{"type": "广播","msg": "your_message"}

<代码>{"类型": "单","elegible": ["IP_1", "???"],"msg": "your_message"}

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

服务器

导入系统从twisted.internet 进口反应堆从twisted.python 导入日志从twisted.web.server 导入站点从twisted.web.static 导入文件从 autobahn.twisted.websocket 导入 WebSocketServerFactory,\WebSocketServer 协议,\听WS类 BroadcastServerProtocol(WebSocketServerProtocol):def onOpen(self):self.factory.register(self)def onConnect(self, request):print("客户端连接:{}".format(request.peer))def onMessage(self, payload, isBinary):如果不是二进制:如果payload.decode('utf8')中的send_broadcast":msg = "已订购发送广播"self.factory.broadcast(msg)def connectionLost(self, reason):WebSocketServerProtocol.connectionLost(self, reason)self.factory.unregister(self)类 BroadcastServerFactory(WebSocketServerFactory):"""简单的广播服务器向所有人广播它收到的任何消息当前连接的客户端."""def __init__(self, url, debug=False, debugCodePaths=False):WebSocketServerFactory.__init__(self, url, debug=debug, debugCodePaths=debugCodePaths)self.clients = []self.tickcount = 0self.tick()定义勾号(自我):self.tickcount += 1self.broadcast("tick %d from server" % self.tickcount)reactor.callLater(1, self.tick)定义注册(自我,客户端):如果客户端不在 self.clients 中:打印(注册客户端{}".格式(client.peer))self.clients.append(client)def unregister(self, client):如果 self.clients 中的客户端:打印(未注册的客户端{}".格式(client.peer))self.clients.remove(客户端)定义广播(自我,味精):print("广播消息'{}' ..".format(msg))对于 self.clients 中的 c:c.sendMessage(msg.encode('utf8'))打印(消息发送到{}".格式(c.peer))类 BroadcastPreparedServerFactory(BroadcastServerFactory):"""功能同上,但优化广播使用prepareMessage 和 sendPreparedMessage."""定义广播(自我,味精):print("广播准备好的消息'{}' ..".format(msg))PreparedMsg = self.prepareMessage(msg)对于 self.clients 中的 c:c.sendPreparedMessage(preparedMsg)print("准备好的消息发送到{}".format(c.peer))如果 __name__ == '__main__':如果 len(sys.argv) >1 和 sys.argv[1] == 'debug':log.startLogging(sys.stdout)调试 = 真别的:调试 = 错误服务器工厂 = 广播服务器工厂# ServerFactory = BroadcastPreparedServerFactoryfactory = ServerFactory("ws://localhost:9000",调试=调试,debugCodePaths=debug)factory.protocol = BroadcastServerProtocolfactory.setProtocolOptions(allowHixie76=True)听WS(工厂)webdir = File(".")网站 = 网站(网站目录)reactor.listenTCP(8080, web)反应器运行()

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

from websocket import create_connectionws = create_connection("ws://localhost:9000")打印发送'send_broadcast'..."ws.send("send_broadcast:PAYLOAD")打印已发送"打印接收..."# 可选结果 = ws.recv() # 可选打印收到 '%s'"% 结果 # 可选ws.close(

)

Totally new to websockets.

I am having a bit of trouble understanding how to interact with python Autobahn / twisted from another application and cannot seem to find any useful examples.

I have a Python application running that needs on certain events to send one of two types of messages. The first is a broadcast message to all users. The second type is to a single specific user.

Using the following two examples I can receive messages and send a response. However I do not need to receive anything from connected clients (other than clients connecting to the websockets server) only send to them.

I have played with: https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/echo

Also (non Autobahn related): https://github.com/opiate/SimpleWebSocketServer

Questions:

1 - Is what I am trying to do possible? Can I have an external application that connects with the Autobahn application / server and broadcasts messages to all connected users or a single user.

2 - If possible can someone point me in the right direction to learn how this can be done?

Thanks

解决方案

First of all the Autobahn project offers an Open-Source implementation of the communication protocol WAMP. WAMP offers the two communication pattern RPC (Remote-Procedure-Call) and PUBSUB (Publish-Subscribe). So in your case it is necessary to figure out which of the two pattern fits your needs.

RPC

According to the WAMP FAQ RPC the RPC involves three Roles. These are:

  • Caller
  • Callee
  • Dealer

In your case, the Callee is the server whereas the Caller (Client) calls a method on the server. This is what apparently works for you. (A return value can be send to the Callee/client). The dealer is responsible for the routing and can be ignored at the moment. So considering the above pattern it seems to be not fitting for your problem.

PUBSUB

The second pattern is PUBSUB. This pattern consists of the three roles (taken from WAMP FAQ PUBSUB):

  • Publisher
  • Subscriber
  • Broker

So what happens is, that the Publisher (Server) publishes events to topics. A Subscriber (Client) can subscribe to a topic of the Publisher. Once an event is published the Subscriber receives the event including the payload. That means that you could offer a topic "Broadcast" and let all clients subscribe to the topic. If required you can send a broadcast message to all clients.

Then you have to deal with the problem of sending a message to single clients (Subscribers). According to the documentation, the publish function for publishing a topic has an optional parameter to give a list of "Clients" that are eligible to receive the event. WAMP Documentation (Class Publish)

--------EDIT--------

It is not clear what is meant by the "external application" and in what language it is supposed to be written. The explained problem by the Author can be solved if the external application is either written in python, JavaScript or Cpp or an Android App using the Autobahn framework with (WAMP).

Autobahn offers, as mentioned in the question, also a websocket protocol implementation. Another approach to solve the problem could be by using Autobahn Websockets and for the "external Application" a Websocket implementation of choice. Autobahn offers for Python and Android Websocket solutions. Of course there are more Websocket libraries or modules available. Java Websocket library, Python Websocket Client module and more...

So lets say the Websocket Server is implemented using the Autobahn framework. The external application is another client connecting to the server and sending a defined string starting with "send_broadcast:PAYLOAD" and the payload appended. On the server you could check the message for the string and if the msg starts with "send_broadcast" you can then send the broadcast to all connected clients. If you want to send the msg to just one client you can define another string like "send_to_single:IP:PAYLOAD" for example. The server implementation could then have another elif branch that checks for the "send_to_single" and call another method, maybe "def send_to_single"?, and pass another argument given the ip of the client. Instead of sending to all clients ,as in the broadcast method, you could send the msg only to the given client. Another way for your own communication protocol would be using JSON. you could define your msg as follows:

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

or

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

On the Server you then load the Payload, check the type and do the further steps.

Server

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()

Client The client is written as well in Python using a different module implementation and still works. It is of course neccessary to comunicate with a Websocket Server using the Websocket protocol.

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(

)

这篇关于高速公路从外部应用程序发送特定于用户的消息和广播消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆