Подтвердить что ты не робот

Autobahn отправляет пользовательские и широковещательные сообщения из внешнего приложения

Совершенно новый для веб-сайтов.

У меня возникли проблемы с пониманием того, как взаимодействовать с Python Autobahn/twisted из другого приложения и не может найти полезных примеров.

У меня есть приложение Python, которое необходимо для определенных событий для отправки одного из двух типов сообщений. Первый - это широковещательное сообщение для всех пользователей. Второй тип - для одного конкретного пользователя.

Используя следующие два примера, я могу получать сообщения и отправлять ответ. Однако мне не нужно ничего получать от подключенных клиентов (кроме клиентов, подключающихся к серверу веб-сайтов), только отправлять их.

Я играл с: https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/echo

Также (не связанный с Autobahn): https://github.com/opiate/SimpleWebSocketServer

Вопросы:

1 - Я пытаюсь сделать это? Могу ли я иметь внешнее приложение, которое подключается к приложению/серверу Autobahn и передает сообщения всем подключенным пользователям или одному пользователю.

2 - Если возможно, кто-то может указать мне в правильном направлении, чтобы узнать, как это можно сделать?

Спасибо

4b9b3361

Ответ 1

Прежде всего, проект Autobahn предлагает реализацию протокола WAMP с открытым исходным кодом. WAMP предлагает два шаблона связи RPC (Remote-Procedure-Call) и PUBSUB (Публикация-Подписка). Поэтому в вашем случае необходимо выяснить, какой из двух шаблонов соответствует вашим потребностям.

RPC

В соответствии с WAMP FAQ RPC RPC включает три роли. Это:

  • Caller
  • вызываемая сторона
  • Dealer

В вашем случае Callee является сервером, тогда как Caller (Client) вызывает метод на сервере. Это то, что, по-видимому, работает для вас. (Возвращаемое значение может быть отправлено на Callee/client). Дилер несет ответственность за маршрутизацию и может быть проигнорирован на данный момент. Поэтому, рассматривая вышеприведенный шаблон, кажется, не подходит для вашей проблемы.

PubSub

Второй шаблон - PUBSUB. Этот шаблон состоит из трех ролей (взятых из WAMP FAQ PUBSUB):

  • Издательство
  • Абонент
  • Брокер

Итак, что происходит, издатель (Сервер) публикует события по темам. Абонент (Клиент) может подписаться на тему издателя. После публикации события Абонент получает событие, включая полезную нагрузку. Это означает, что вы можете предложить тему "Трансляция" и позволить всем клиентам подписаться на эту тему. При необходимости вы можете отправить широковещательное сообщение всем клиентам.

Затем вам нужно решить проблему отправки сообщения отдельным клиентам (подписчикам). Согласно документации, функция публикации для публикации темы имеет необязательный параметр, чтобы предоставить список "Клиентов", которые имеют право на получение события. Документация WAMP (публикация класса)

-------- РЕДАКТИРОВАТЬ --------

Неясно, что подразумевается под "внешним приложением" и на каком языке он должен быть написан. Объясненная проблема Автором может быть решена, если внешнее приложение либо написано на языке python, JavaScript или Cpp, либо в приложении Android, используя фреймворк Autobahn (WAMP).

Autobahn предлагает, как упоминалось в вопросе, также реализацию протокола websocket. Другим подходом к решению проблемы может быть использование веб-сайтов Autobahn, а для "внешнего приложения" - выбор реализации Websocket. Autobahn предлагает решения Python и Android Websocket. Конечно, есть больше библиотек или модулей Websocket. Библиотека Java Websocket, Клиентский модуль Python Websocket и многое другое...

Итак, скажем, сервер Websocket реализован с использованием инфраструктуры Autobahn. Внешнее приложение - это другой клиент, подключающийся к серверу и отправляющий определенную строку, начинающуюся с "send_broadcast: PAYLOAD" и добавленной полезной нагрузки. На сервере вы можете проверить сообщение для строки, и если msg начинается с "send_broadcast", вы можете отправить трансляцию всем подключенным клиентам. Если вы хотите отправить сообщение только одному клиенту, вы можете определить другую строку, например, "send_to_single: IP: PAYLOAD". После этого серверная реализация может иметь другую ветвь elif, которая проверяет "send_to_single" и вызывает другой метод, возможно, "def send_to_single"?, и передать другой аргумент, заданный ip клиента. Вместо отправки всем клиентам, как и в широковещательном методе, вы можете отправить сообщение только данному клиенту. Другой способ для вашего собственного протокола связи - использовать JSON. вы можете определить свой msg следующим образом:

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

Client Клиент также написан на 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(

)