我正在尝试在发送UDP数据包后立即关闭传输,我得到Exception in callback _SelectorDatagramTransport._read_ready()
import asyncio
class MyProtocol:
def __init__(self, message, loop):
self.message = message
self.loop = loop
self.transport = None
def connection_made(self, transport):
self.transport = transport
print("Send:", self.message)
self.transport.sendto(self.message.encode())
self.transport.close() # <----------
def error_received(self, exc):
print('Error received', exc)
def connection_lost(self, exc):
print("Socket closed, stop the event loop")
self.loop.stop()
loop = asyncio.get_event_loop()
message = "hello"
connect = loop.create_datagram_endpoint(lambda: MyProtocol(message, loop), remote_addr=('127.0.0.1', 2222))
transport, protocol = loop.run_until_complete(connect)
loop.run_forever()
我得到的完整堆栈跟踪是在CPython 3.5.1中运行上面的代码片段时:
Socket closed, stop the event loop
Exception in callback _SelectorDatagramTransport._read_ready()
handle: <Handle _SelectorDatagramTransport._read_ready()>
Traceback (most recent call last):
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 1002, in _read_ready
data, addr = self._sock.recvfrom(self.max_size)
AttributeError: 'NoneType' object has no attribute 'recvfrom'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/events.py", line 125, in _run
self._callback(*self._args)
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 1008, in _read_ready
self._fatal_error(exc, 'Fatal read error on datagram transport')
File "/home/ecerulm/.pyenv/versions/3.5.1/lib/python3.5/asyncio/selector_events.py", line 587, in _fatal_error
self._loop.call_exception_handler({
AttributeError: 'NoneType' object has no attribute 'call_exception_handler'
我认为只有在UDP数据包被主动拒绝且ICMP目的地无法访问(我对此不感兴趣)时才会生成异常。
所以问题是这样做的正确方法是什么。发送后我不再对这种连接感兴趣所以我想尽快摆脱运输。 DatagramTransport.sendto()
的文档只是说方法没有阻止。但是我怎么知道发送完成的时间? (完整我的意思是什么时候交给操作系统,而不是交付到远程)。
是否有任何其他asyncio
协程异步发送UDP数据包await
(甚至可能跳过整个create_datagram_endpoint
)?
答案 0 :(得分:1)
是否有任何其他asyncio协程异步发送UDP数据包
await
?
我会基于DatagramTransport
来源,将其包装在Future
中以使其变得可屈服/等待。它会在出错时引发异常,并在成功时返回True
。 example PoC code:
import asyncio
import socket
class UDPClient():
def __init__(self, host, port, loop=None):
self._loop = asyncio.get_event_loop() if loop is None else loop
self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self._sock.setblocking(False)
self._addr = (host, port)
self._future = None
self._data = None
def sendto(self, data):
self._future = asyncio.Future(loop=self._loop)
self.data = data if isinstance(data, bytes) else str(data).encode('utf-8')
loop.add_writer(self._sock.fileno(), self._sendto)
return self._future
def _sendto(self):
try:
self._sock.sendto(self.data, self._addr)
except (BlockingIOError, InterruptedError):
return
except OSError as exc:
self.abort(exc)
except Exception as exc:
self.abort(exc)
else:
self.close()
self._future.set_result(True)
def abort(self, exc):
self.close()
self._future.set_exception(exc)
def close(self):
self._loop.remove_writer(self._sock.fileno())
self._sock.close()
比简单的例子看起来像:
@asyncio.coroutine
def test():
yield from UDPClient('127.0.0.1', 1234).sendto('ok')
# or 3.5+ syntax
# async def test():
# await UDPClient('127.0.0.1', 1234).sendto('ok')
loop = asyncio.get_event_loop()
loop.run_until_complete(test())