我有这段代码,我想连接到websocket并通过发送心跳来保持连接活跃。在执行此操作时,我还希望能够将有效负载发送到websocket,但是我一直卡在等待keepAlive
协程的结果
import asyncio
import websockets
ws=#path goes here
async def sendHeartbeat(socket):
message={
"op": 1,
"d": 0
}
await socket.send(json.dumps(message))
print("heartbeat sent")
async def keepAlive(socket):
while 1==1:
await sendHeartbeat(socket)
resp=await(socket.recv())
print(resp)
if json.loads(resp)["op"]==11:
#the server sent an ack back
print("ack received")
else:
socket.close()
await asyncio.sleep(10)
async def beginSocket(loop):
async with websockets.connect(ws) as socket:
print(await socket.recv())
await keepAlive(socket)
#payloads would be sent here
print("Hey payloads being sent and stuff")
loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))
但是,使用这段代码,等待keepAlive
后的打印语句永远不会打印出来。我怎样才能使代码不等待keepAlive
的结果?
答案 0 :(得分:0)
虽然keepAlive(socket)
会立即返回,但由于keepAlive
是一个couroutine,await keepAlive(socket)
永远不会返回,因为keepAlive()
包含无限循环。
不要使用await
,请尝试asyncio.ensure_future(keepAlive(socket))
。
如果您确实想使用await keepAlive(socket)
,请尝试从其他地方发送有效负载(可能事先使用asyncio.ensure_future(send_payload(socket))
)。
答案 1 :(得分:0)
在这种情况下,您在概念上通过同一套接字进行两次单独的对话。一个对话就是你的心跳和回复信息。另一个是您要发送的其他数据包。
我会保留3个独立的顶级(即他们直接向事件循环报告)任务。我希望他们都能引用一些协调员,所以当有时间时,他们中的任何一个都可以取消所有其他协调员。
keepAlive
功能。多路复用任务的工作是将消息路由到适当的任务。心跳任务应该只获得心跳响应,而另一个任务应该获取所有其他消息。
由于websockets已经对消息进行了框架处理,因此您只能send
或recv
整条消息,因此它可能拥有的另一项工作无关紧要。
这是你可以写这个的一种方式。
import asyncio
import websockets
ws=#path goes here
class RoutingTask(object):
def __init__(self, sock, defaultQueue, **kwargs):
super().__init__(**kwargs)
self.sock = sock
self.defaultQueue = defaultQueue # The queue all messages not otherwise matched go to.
self.matchers = []
async def run(self):
while True:
msg = await self.sock.recv()
msg = json.loads(msg)
matched = False
for matcher, queue in matchers:
if matcher(msg):
await queue.put(msg)
matched = True
break
if not matched:
await self.defaultQueue.put(msg)
def addMatcher(self, matcher, queue):
el = (matcher, queue)
self.matchers.append(el)
async def heartbeatTask(wssock, incomingq):
message=json.dumps({
"op": 1,
"d": 0
}) # Do this just once.
while True:
await wssock.send(message)
print("heartbeat sent")
response = await asyncio.wait_for(incomingq.get(), 10) # Wait 10 seconds for response.
assert response['op'] == 11
print("heartbeat response received.")
await asyncio.sleep(10) # Wait 10 seconds to send another heartbeat.
async def beginSocket(loop):
def heartbeatMatcher(jsondict):
return jsondict.get('op', None) == 11
async with websockets.connect(ws) as socket:
myq = asyncio.Queue(maxsize=1)
heartbeatq = asyncio.Queue(maxsize=1)
router = RoutingTask(socket, myq)
router.addMatcher(heartbeatMatcher, heartbeatq)
router = asyncio.ensure_future(router.run())
heartbeat = asyncio.ensure_future(heartbeatTask(socket, heartbeatq)
print(await myq.get())
#payloads would be sent here
print("Hey payloads being sent and stuff")
heartbeat.cancel() # Stop the heartbeat
router.cancel() # Stop the router task
loop = asyncio.get_event_loop()
loop.run_until_complete(beginSocket(loop))
这里有一些问题。如果抛出异常,heartbeat
和router
任务可能不会被取消。他们也没有很好的方法将问题报告回主beginSocket
任务。这基本上是一种快速和肮脏的一次性,以演示如何做你想做的事。
在我看来,asyncio.ensure_future
被错误命名。它的作用是告诉事件循环它需要保持运行的新事物。它基本上是启动了一个线程的协同程序。