I am trying to send a base64 encoded PNG images to a JavaScript client via asyncio websockets server in python. Right now I am just looping through images from memory to create a "video player" on the JavaScript side and its working... however, the first image that comes through throws a RuntimeError on my python server. it then operates as expected so very hard to pinpoint the problem..
ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<WebSocketCommonProtocol.send() done, defined at /usr/local/lib/python3.7/dist-packages/websockets/protocol.py:521> exception=RuntimeError('Task <Task pending coro=<WebSocketCommonProtocol.send() running at /usr/local/lib/python3.7/dist-packages/websockets/protocol.py:567> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.7/asyncio/tasks.py:440] created at /usr/lib/python3.7/asyncio/tasks.py:361> got Future <Future pending> attached to a different loop') created at /usr/lib/python3.7/asyncio/tasks.py:361>
source_traceback: Object created at (most recent call last):
File "/usr/lib/python3.7/threading.py", line 885, in _bootstrap
self._bootstrap_inner()
File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "../comm/mqtt.py", line 320, in __process_messages
response_data = self._msg_callback_dispatch[t](in_msg.body)
File "SiteManagerOp.py", line 385, in on_image_callback
coro = asyncio.run(self.tx_UI_device_image(data.get('did'), data.get('img')), debug=True)
File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.7/asyncio/base_events.py", line 571, in run_until_complete
self.run_forever()
File "/usr/lib/python3.7/asyncio/base_events.py", line 539, in run_forever
self._run_once()
File "/usr/lib/python3.7/asyncio/base_events.py", line 1767, in _run_once
handle._run()
File "/usr/lib/python3.7/asyncio/events.py", line 88, in _run
self._context.run(self._callback, *self._args)
File "Manager.py", line 181, in tx_UI_device_image
await self.sockserver.send_to_clients(json.dumps(img_msg))
File "/home/mendel/webserver/SocketServer.py", line 39, in send_to_clients
await asyncio.wait([client.send(message) for client in self.clients])
File "/usr/lib/python3.7/asyncio/tasks.py", line 361, in wait
fs = {ensure_future(f, loop=loop) for f in set(fs)}
File "/usr/lib/python3.7/asyncio/tasks.py", line 361, in <setcomp>
fs = {ensure_future(f, loop=loop) for f in set(fs)}
Traceback (most recent call last):
File "/usr/local/lib/python3.7/dist-packages/websockets/protocol.py", line 567, in send
await self.write_frame(True, opcode, data)
File "/usr/local/lib/python3.7/dist-packages/websockets/protocol.py", line 1077, in write_frame
await self._drain()
File "/usr/local/lib/python3.7/dist-packages/websockets/protocol.py", line 318, in _drain
await self._drain_helper()
File "/usr/local/lib/python3.7/dist-packages/websockets/protocol.py", line 299, in _drain_helper
await waiter
RuntimeError: Task <Task pending coro=<WebSocketCommonProtocol.send() running at /usr/local/lib/python3.7/dist-packages/websockets/protocol.py:567> cb=[_wait.<locals>._on_completion() at /usr/lib/python3.7/asyncio/tasks.py:440] created at /usr/lib/python3.7/asyncio/tasks.py:361> got Future <Future pending> attached to a different loop
Python Code:
#storing images to memory..
img_data = []
i=1
while i < 300:
with open(f"pngs/out{i}.png", "rb") as f:
img_data.append(base64.b64encode(f.read()).decode('utf-8'))
i+=1
#non-async call back for recieving images
def on_image_callback(self, data=None):
print (f"rx image:", data.get('did'))
coro = asyncio.run(self.tx_UI_device_image(data.get('did'), data.get('img')), debug=True)
#async initiates send_to_client as JSON payload..
async def tx_UI_device_image(self, did, img):
img_msg = {'image':{'did':did, 'img_data':img}}
print ("img size: ", len(img))
await self.sockserver.send_to_clients(json.dumps(img_msg))
#socket server handler...
class WebsocketServer:
def __init__(self, host, port, onconnect_callback, rxdata_callback):
self.start_server = websockets.serve(self.ws_handler, host, port)
self.loop = asyncio.get_event_loop()
self.onconnect = onconnect_callback
self.ondata_Rx = rxdata_callback
self.clients = set()
def start(self):
self.loop.run_until_complete(self.start_server)
self.loop.run_forever()
async def register(self, ws: WebSocketServerProtocol) -> None:
self.clients.add(ws)
logging.info(f'{ws.remote_address} connects.')
await self.onconnect()
async def unregister(self, ws: WebSocketServerProtocol) -> None:
self.clients.remove(ws)
logging.info(f'{ws.remote_address} disconnects.')
async def send_to_clients(self, message: str) -> None:
if self.clients:
await asyncio.wait([client.send(message) for client in self.clients])
async def ws_handler(self, ws: WebSocketServerProtocol, uri: str) -> None:
await self.register(ws)
try:
await self.distribute(ws)
finally:
await self.unregister(ws)
async def distribute(self, ws: WebSocketServerProtocol) -> None:
async for message in ws:
await self.ondata_Rx(json.loads(message))
JavaScript:
s.onmessage = function(e) {
//console.log("got: " + e.data);
var data = JSON.parse(e.data);
for (var key in data) {
if (data.hasOwnProperty(key)) { // this will check if key is owned by data object and not by any of it's ancestors
if (key == "image"){
console.log(data[key].did);
var image = new Image();
image.src =('data:image/png;base64,' + data[key].img_data);
var player = document.getElementById(data[key].did + '-player');
player.setAttribute('src', image.src)
}
}
}
I believe the problem is the asyncio.run call in a different thread affecting the event_loop or creating a new one rather, but I can't find any other way around this. It works with a much smaller message payload or if i just send a bunch of chars like ('A' * 300000)
What is causing this error and how do I resolve or workaround?
UPDATE: Found Solution
instead of calling asyncio.run()
anytime i needed to call something outside of an async function - I used asyncio.run_coroutine_threadsafe(<coroutine>, loop=self.socketserver.loop)
不要在任何时候调用 async 函数之外的东西(或即在不同的线程中)调用asyncio.run()
- 改用它并指定要定位的事件循环:
asyncio.run_coroutine_threadsafe(<coroutine>, loop=self.socketserver.loop)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.