【问题标题】:How can I implement an interactive websocket client with autobahn asyncio?如何使用高速公路异步实现交互式 websocket 客户端?
【发布时间】:2014-11-03 02:04:23
【问题描述】:

我正在尝试使用 autobahn|python 实现 websocket/wamp 客户端 和asyncio,虽然它有点工作,但有些部分有 躲过了我。

我真正想做的是在 qt5/QML 中实现 WAMP,但这 目前看来是一条更容易的道路。

这个主要从网上复制的简化客户端确实有效。它读取 onJoin 发生时的时间服务。

我想做的是从外部源触发此读取。

我采用的复杂方法是​​在 线程,然后通过套接字发送命令以触发读取。一世 到目前为止无法弄清楚将例程/协程放在哪里 可以从阅读器例程中找到它。

我怀疑有更简单的方法可以解决这个问题,但我还没有找到 然而。欢迎提出建议。

#!/usr/bin/python3
try:
    import asyncio
except ImportError:
    ## Trollius >= 0.3 was renamed
    import trollius as asyncio

from autobahn.asyncio import wamp, websocket
import threading
import time
from socket import socketpair

rsock, wsock = socketpair()

def reader() :
    data = rsock.recv(100)
    print("Received:", data.decode())

class MyFrontendComponent(wamp.ApplicationSession):
    def onConnect(self):
        self.join(u"realm1")



    @asyncio.coroutine
    def onJoin(self, details):
        print('joined')
        ## call a remote procedure
        ##
        try:
           now = yield from self.call(u'com.timeservice.now')
        except Exception as e:
           print("Error: {}".format(e))
        else:
           print("Current time from time service: {}".format(now))



    def onLeave(self, details):
        self.disconnect()

    def onDisconnect(self):
        asyncio.get_event_loop().stop()



def start_aloop() :
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    transport_factory = websocket.WampWebSocketClientFactory(session_factory,
                    debug = False,
                    debug_wamp = False)
    coro = loop.create_connection(transport_factory, '127.0.0.1', 8080)
    loop.add_reader(rsock,reader)
    loop.run_until_complete(coro)
    loop.run_forever()
    loop.close()

if __name__ == '__main__':
    session_factory = wamp.ApplicationSessionFactory()
    session_factory.session = MyFrontendComponent

    ## 4) now enter the asyncio event loop
    print('starting thread')
    thread = threading.Thread(target=start_aloop)
    thread.start()
    time.sleep(5)
    print("IN MAIN")
    # emulate an outside call
    wsock.send(b'a byte string')

【问题讨论】:

  • 那么,您希望能够通过某种外部方式触发客户端对 timeservice 进行 RPC 调用?

标签: python autobahn python-asyncio


【解决方案1】:

您可以使用loop.sock_accept 在事件循环内异步侦听套接字。你可以调用一个协程来设置onConnectonJoin内部的socket:

try:
    import asyncio
except ImportError:
    ## Trollius >= 0.3 was renamed
    import trollius as asyncio

from autobahn.asyncio import wamp, websocket
import socket

class MyFrontendComponent(wamp.ApplicationSession):
    def onConnect(self):
        self.join(u"realm1")

    @asyncio.coroutine
    def setup_socket(self):
        # Create a non-blocking socket
        self.sock = socket.socket()
        self.sock.setblocking(0)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind(('localhost', 8889))
        self.sock.listen(5)
        loop = asyncio.get_event_loop()
        # Wait for connections to come in. When one arrives,
        # call the time service and disconnect immediately.
        while True:
            conn, address = yield from loop.sock_accept(self.sock)
            yield from self.call_timeservice()
            conn.close()

    @asyncio.coroutine
    def onJoin(self, details):
        print('joined')
        # Setup our socket server
        asyncio.async(self.setup_socket())

        ## call a remote procedure
        ##
        yield from self.call_timeservice()

    @asyncio.coroutine
    def call_timeservice(self):
        try:
           now = yield from self.call(u'com.timeservice.now')
        except Exception as e:
           print("Error: {}".format(e))
        else:
           print("Current time from time service: {}".format(now))

    ... # The rest is the same

【讨论】:

    【解决方案2】:

    感谢达诺的回复。不是我需要的解决方案,但它为我指明了正确的方向。是的,我希望客户端可以从外部触发器进行远程 RPC 调用。

    我想出了以下方法,它允许我为特定调用传递一个字符串(尽管现在只实现了一个)

    这是我想出的,虽然我不确定它有多优雅。

    import asyncio
    from autobahn.asyncio import wamp, websocket
    import threading
    import time
    import socket
    
    
    rsock, wsock = socket.socketpair()
    
    class MyFrontendComponent(wamp.ApplicationSession):
        def onConnect(self):
            self.join(u"realm1")
    
        @asyncio.coroutine
        def setup_socket(self):
            # Create a non-blocking socket
            self.sock = rsock
            self.sock.setblocking(0)
            loop = asyncio.get_event_loop()
            # Wait for connections to come in. When one arrives,
            # call the time service and disconnect immediately.
            while True:
                rcmd = yield from loop.sock_recv(rsock,80)
                yield from self.call_service(rcmd.decode())
    
        @asyncio.coroutine
        def onJoin(self, details):
            # Setup our socket server
            asyncio.async(self.setup_socket())
    
    
        @asyncio.coroutine
        def call_service(self,rcmd):
            print(rcmd)
            try:
               now = yield from self.call(rcmd)
            except Exception as e:
               print("Error: {}".format(e))
            else:
               print("Current time from time service: {}".format(now))
    
    
    
        def onLeave(self, details):
            self.disconnect()
    
        def onDisconnect(self):
            asyncio.get_event_loop().stop()
    
    
    
    def start_aloop() :
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        transport_factory = websocket.WampWebSocketClientFactory(session_factory,
                        debug = False,
                        debug_wamp = False)
        coro = loop.create_connection(transport_factory, '127.0.0.1', 8080)
        loop.run_until_complete(coro)
        loop.run_forever()
        loop.close()
    
    if __name__ == '__main__':
        session_factory = wamp.ApplicationSessionFactory()
        session_factory.session = MyFrontendComponent
    
        ## 4) now enter the asyncio event loop
        print('starting thread')
        thread = threading.Thread(target=start_aloop)
        thread.start()
        time.sleep(5)
        wsock.send(b'com.timeservice.now')
        time.sleep(5)
        wsock.send(b'com.timeservice.now')
        time.sleep(5)
        wsock.send(b'com.timeservice.now')
    

    【讨论】:

    • 您可以使用asyncio.sleeploop.socket_sendall 以更加asyncio 友好的方式实现wsock。这样你就不需要后台线程了。相反,您只需使用 asyncio.async 调度一个协程,该协程调用 yield from asyncio.sleep(5),后跟 yield from loop.sock_sendall(wsock, b'com.timeservice.now')
    • 谢谢,我会继续研究这个,但我只使用了几天,还没有完全跟上进度。我已将循环置于后台,因为此代码在 pyotherside 内部使用,而前台函数由 QML 代码调用。可能有更好的方法可以做到这一点,我怀疑它会随着时间变得更加清晰。
    • 啊,如果您打算将此代码集成到无法插入asyncio 事件循环的应用程序中,那么您的想法是正确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-10
    • 2013-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多