Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:同步程序中的WebSocket_Python_Websocket_Python Asyncio - Fatal编程技术网

Python:同步程序中的WebSocket

Python:同步程序中的WebSocket,python,websocket,python-asyncio,Python,Websocket,Python Asyncio,我有一个bog标准的同步python程序,需要能够从WebSocket读取数据并用数据更新GUI。然而,asyncio爬行经常让我绊倒 我如何制作一个模块: 接受对多个源的多个订阅 只要有数据,就向请求者发送更新 每个URL只打开一个websocket连接 如果websocket关闭,则重置websocket 以下是我已经做过的,但它在很多方面都失败了: run\u forever()意味着循环在订阅完成之前被卡住,然后handle()在循环时被卡住 它似乎不想在套接字关闭时重新启动它们,因为w

我有一个bog标准的同步python程序,需要能够从WebSocket读取数据并用数据更新GUI。然而,asyncio爬行经常让我绊倒

我如何制作一个模块:

  • 接受对多个源的多个订阅
  • 只要有数据,就向请求者发送更新
  • 每个URL只打开一个websocket连接
  • 如果websocket关闭,则重置websocket
  • 以下是我已经做过的,但它在很多方面都失败了:

  • run\u forever()
    意味着循环在订阅完成之前被卡住,然后
    handle()
    循环时被卡住
  • 它似乎不想在套接字关闭时重新启动它们,因为websockets对象没有连接的
    属性(没有s的websocket有,但我不清楚区别,也无法在线找到信息)
  • 我绝对不确定我的方法是否正确
  • 这件事已经争论了好几个星期了。如果你能给我指点,我会很感激的

    class WSClient():
        subscriptions = set()
        connections = {}
        started = False
    
        def __init__(self):
            self.loop = asyncio.get_event_loop()
    
        def start(self):
            self.started = True
            self.loop.run_until_complete(self.handle())
            self.loop.run_until_forever()  # problematic, because it does not allow new subscribe() events
    
        async def handle(self):
            while len(self.connections) > 0:
                # listen to every websocket
                futures = [self.listen(self.connections[url]) for url in self.connections]
                done, pending = await asyncio.wait(futures)
    
                # the following is apparently necessary to avoid warnings
                # about non-retrieved exceptions etc
                try:
                    data, ws = done.pop().result()
                except Exception as e:
                    print("OTHER EXCEPTION", e)
    
                for task in pending:
                    task.cancel()
    
        async def listen(self, ws):
            try:
                async for data in ws:
                    data = json.loads(data)
                    # call the subscriber (listener) back when there's data
                    [s.listener._handle_result(data) for s in self.subscriptions if s.ws == ws]
            except Exception as e:
                print('ERROR LISTENING; RESTARTING SOCKET', e)
                await asyncio.sleep(2)
                self.restart_socket(ws)
    
        def subscribe(self, subscription):
            task = self.loop.create_task(self._subscribe(subscription))
            asyncio.gather(task)
    
            if not self.started:
                self.start()
    
        async def _subscribe(self, subscription):
            try:
                ws = self.connections.get(subscription.url, await websockets.connect(subscription.url))
                await ws.send(json.dumps(subscription.sub_msg))
    
                subscription.ws = ws
                self.connections[subscription.url] = ws
                self.subscriptions.add(subscription)
            except Exception as e:
                print("ERROR SUBSCRIBING; RETRYING", e)
                await asyncio.sleep(2)
                self.subscribe(subscription)
    
        def restart_socket(self, ws):
            for s in self.subscriptions:
                if s.ws == ws and not s.ws.connected:
                    print(s)
                    del self.connections[s.url]
                    self.subscribe(s)
    
    我有一个bog标准的同步python程序,需要能够从WebSocket读取数据并用数据更新GUI。然而,asyncio爬行经常让我绊倒

    正如您提到的GUI,那么它可能不是“bog标准同步python程序”。通常,GUI程序有一个非阻塞事件驱动的主线程,它允许并发用户行为和回调。这与asyncio非常相似,通常asyncio与GUI一起使用GUI特定的事件循环来替换asyncio中的默认事件循环是一种常见的方式,这样您的asyncio协程就可以在GUI事件循环中运行,并且您可以避免调用
    run\u ever()
    阻塞所有内容

    另一种方法是在单独的线程中运行asyncio事件循环,以便程序可以同时等待websocket数据和用户单击。我已将您的代码改写如下:

    import asyncio
    import threading
    import websockets
    import json
    
    
    class WSClient(threading.Thread):
    
        def __init__(self):
            super().__init__()
            self._loop = None
            self._tasks = {}
            self._stop_event = None
    
        def run(self):
            self._loop = asyncio.new_event_loop()
            self._stop_event = asyncio.Event(loop=self._loop)
            try:
                self._loop.run_until_complete(self._stop_event.wait())
                self._loop.run_until_complete(self._clean())
            finally:
                self._loop.close()
    
        def stop(self):
            self._loop.call_soon_threadsafe(self._stop_event.set)
    
        def subscribe(self, url, sub_msg, callback):
            def _subscribe():
                if url not in self._tasks:
                    task = self._loop.create_task(
                        self._listen(url, sub_msg, callback))
                    self._tasks[url] = task
    
            self._loop.call_soon_threadsafe(_subscribe)
    
        def unsubscribe(self, url):
            def _unsubscribe():
                task = self._tasks.pop(url, None)
                if task is not None:
                    task.cancel()
    
            self._loop.call_soon_threadsafe(_unsubscribe)
    
        async def _listen(self, url, sub_msg, callback):
            try:
                while not self._stop_event.is_set():
                    try:
                        ws = await websockets.connect(url, loop=self._loop)
                        await ws.send(json.dumps(sub_msg))
                        async for data in ws:
                            data = json.loads(data)
    
                            # NOTE: please make sure that `callback` won't block,
                            # and it is allowed to update GUI from threads.
                            # If not, you'll need to find a way to call it from
                            # main/GUI thread (similar to `call_soon_threadsafe`)
                            callback(data)
                    except Exception as e:
                        print('ERROR; RESTARTING SOCKET IN 2 SECONDS', e)
                        await asyncio.sleep(2, loop=self._loop)
            finally:
                self._tasks.pop(url, None)
    
        async def _clean(self):
            for task in self._tasks.values():
                task.cancel()
            await asyncio.gather(*self._tasks.values(), loop=self._loop)
    

    您可以尝试龙卷风和高速公路扭曲的websockets

    谢谢。我还有一些更紧迫的事情要处理,但我会尝试你的解决方案,并让你知道它是如何工作的谢谢你的建议。至少现在它可以与服务器通信了,只是做了一个小改动:我在订阅时创建新的事件循环,而不是在运行时。否则为空。然而,它仍然以某种方式阻塞GUI,尽管WSClient在一个单独的线程中运行,我使用
    GLib.idle\u add()
    向GUI发送更新信号。事实上,GUI的任何部分都没有初始化,所以我仍然想知道是怎么回事。它正在打印来自websocket的响应,但GUI根本没有显示出来。这可能是因为启动ws客户端后调用了
    Gtk.main()
    。但是,如果我之前启动它,主循环不允许运行其他循环。因此,也许你的第一个解决方案是正确的,我只是不知道如何(现在)