Ответ 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(
)