簡體   English   中英

python 異步循環每 15 秒發送一次 udp 有效負載,並在 60 秒后停止嘗試

[英]python async loop to send udp payload every 15 seconds, and stop trying after 60 seconds

我的目標是創建一個異步循環,連接到 UDP ip 地址並接收響應。 但是,如果在 15 秒內沒有收到響應,請重新發送請求,並在 60 秒后停止嘗試。

主文件

async def main():
    tasks = []
    for i, l in zip(result["tr"], range(len(result["tr"]))):
        task = create_task(udp.connect(urlparse(i).hostname, urlparse(i).port))
        tasks.append(task)

    done, pending = await wait(tasks)
    for i in done:
        print(i.result())

udpClient.py

async def connect(udp, port):
    payload = b'sample_data_input'
    on_conn_lost, datagram_data = loop.create_future(), loop.create_future()

    transport, protocol = await self.default_event_loop.create_datagram_endpoint(
        lambda: ProtocolFactoryUDP(payload, on_conn_lost, datagram_data),
        remote_addr=(str(udp), int(port)))

    try:
        await on_conn_lost
        return datagram_data
    except Exception as e:
        print(e)

ProtocolFactoryUDP -> 這與 python.org 上的示例相同

class ProtocolFactoryUDP:
    def __init__(self, message, on_con_lost):
        self.message = message
        self.on_con_lost = on_con_lost
        self.transport = None
        self.datagram_data = datagram_data

    def connection_made(self, transport):
        self.transport = transport
        print('Send:', self.message)
        self.transport.sendto(self.message.encode())

    def datagram_received(self, data, addr):
        print("Received:", data.decode())
        self.datagram_data.set_result((data, addr))
        print("Close the socket")
        self.transport.close()

    def error_received(self, exc):
        print('Error received:', exc)

    def connection_lost(self, exc):
        print("Connection closed")
        self.on_con_lost.set_result(True)

目前,此代碼允許我連接到 UDP 目標並打印出響應。

然而,這並沒有達到我的目標,因為如果服務器沒有響應任何東西/響應時間太長,udpClient 最終會永遠等待在那里。 如何創建一個異步循環,以便如果在 15 秒內未收到響應,則再次重新發送請求,並在嘗試 60 秒后以空有效負載退出(或正常退出)。

使用來自 Paul 的更新代碼編輯 1

主文件

for i, l in zip(result["tr"], range(len(result["tr"]))):
    task = create_task(udp.connect(urlparse(i).hostname, urlparse(i).port))
    tasks.append(task)

for f in as_completed(tasks):
    result = await f

udpClient.py

...
for _ in range(4):
    try:
        transport, protocol = await self.default_event_loop.create_datagram_endpoint(
            lambda: ProtocolFactoryUDP(payload, on_conn_lost, datagram_data),
            remote_addr=(str(udp), int(port)))

        await asyncio.wait_for(on_conn_lost, 15.0)  # -> Met with CancelledError here.. 
        return datagram_data
    except TimeoutError:
        pass

你已經完美地描述了你想要做什么,這通常是最難的部分。 要將其簡化為代碼,您可以查看 asyncio.wait_for function,以及 asyncio.as_completed function。 這是我認為你想要的草圖。 我將 UDP 詳細信息留給您。

wait_for function 采用超時值(在您的情況下為 15 秒)。 你想嘗試 4 次,所以你只需要一個簡單的 for 循環。 您捕獲 TimeoutError 並重復循環; 讓其他錯誤傳播回調用者。 如果您沒有超時,您只需返回,這將終止任務。

您可以在結果完成時打印結果,或者使用 asyncio.gather(return_exceptions=True) 等到它們全部完成。

async def main():
    tasks = []
    for i, l in zip(result["tr"], range(len(result["tr"]))):
        coro = my_task(udp.connect(urlparse(i).hostname, urlparse(i).port))
        tasks.append(asyncio.create_task(coro))

    for i in asyncio.as_completed(tasks):
        print(i.result())

async def my_task(host, port):
    for _ in range(4):
        try:
            await asyncio.wait_for(do_something_with_udp(host, port), 15.0)
            return
        except asyncio.TimeoutError:
            pass

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM