python - How can I close a DatagramTransport as soon one datagram is sent? -
i'm trying close transport right after sending udp packet , i'm getting 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 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()
the full stack trace while running snippet above in cpython 3.5.1 is:
socket closed, stop 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 above exception, 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'
i believe exception generated if udp packet actively refused, icmp destination unreachable (which i'm not interested in).
so question right way of doing this. i'm not interested in connection anymore after sending want rid of transport possible. documentation datagramtransport.sendto()
says methods doesn't block. how know when sending completed? (and complete mean when handed on os, not delivered remote).
is there other asyncio
coroutine send udp packet asynchronously , simple await
(maybe skipping whole create_datagram_endpoint
) ?
is there other asyncio coroutine send udp packet asynchronously , simple
await
?
i would, base on datagramtransport
source, wrap in future
yieldable/awaitable. raise exception on error , return true
on success. example poc code:
import asyncio import socket class udpclient(): def __init__(self, host, port, loop=none): self._loop = asyncio.get_event_loop() if loop 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 exc: self.abort(exc) except exception 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()
than simple example like:
@asyncio.coroutine def test(): yield 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())
Comments
Post a Comment