msg260580 - (view) |
Author: Simon Bernier St-Pierre (sbstp) * |
Date: 2016-02-20 22:50 |
I want to receive data on a UDP socket that was bound, without blocking the event loop. I've looked through the asyncio docs, and I haven't found a way of doing that using the coroutine API (yield from/await). There is a sock_recv method on BaseEventLoop which is a coroutine, it seems like sock_recvfrom was never implemented. I don't have a patch for this right now, I wanted to know what people thought of adding support for this. |
|
|
msg260582 - (view) |
Author: Guido van Rossum (gvanrossum) *  |
Date: 2016-02-20 23:59 |
You should be able to do this by calling create_datagram_endpoint(), passing it a custom DatagramProtocol subclass whose datagram_received() stores the data in the result of a Future. You can then wait for the Future to wait for the data (assuming it ever arrives :-). |
|
|
msg260585 - (view) |
Author: Simon Bernier St-Pierre (sbstp) * |
Date: 2016-02-21 00:23 |
That could work. I came up with this class MyProtocol(aio.DatagramProtocol): def __init__(self, fut): self._fut = fut def datagram_received(self, data, addr): self.fut.set_result((data, addr)) fut = aio.Future() loop.create_datagram_endpoint(lambda: MyProtocol(fut), ...) yield from fut 1. Is there a better way of sharing the future between the protocol and the main function? 2. This might be inefficient because I have to create a new endpoint every time I want to receive a packet. I might be able to implement a scheme where I give the protocol a new future after every packet, but it's kind of cumbersome. If I wrote the patch to make sock_recvfrom work, can it get merged or must it go through the PEP process? |
|
|
msg260586 - (view) |
Author: Guido van Rossum (gvanrossum) *  |
Date: 2016-02-21 00:36 |
I won't make you go through the PEP process, but I do think it's a bad idea to add this. After all datagrams aren't guaranteed to arrive, so, whil I don't know what protocol you're trying to implement, I think you're probably better off writing the whole thing as a DatagramProtocol subclass that handles all incoming packets. If you need help writing your app's code it's probably better to ask around on a mailing list. |
|
|
msg260588 - (view) |
Author: Simon Bernier St-Pierre (sbstp) * |
Date: 2016-02-21 00:47 |
I want to have a loop that receives data like this: socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) socket.bind(('0.0.0.0', port)) socket.setblocking(False) while True: data, addr = await loop.sock_recvfrom(sock, 4096) # process packet It's pretty similar to what the blocking code would look like, but it allows me to keep everything on a single thread without blocking. It could be done with the Protocol API, but I'd rather use the shiny new async/await API. |
|
|
msg260596 - (view) |
Author: Martin Panter (martin.panter) *  |
Date: 2016-02-21 07:41 |
If your event loop supports it, maybe you could use add_reader() etc as a workaround (roughly based off a different function of my own; this version completely untested): async def sock_recvfrom(nonblocking_sock, *pos, loop, **kw): while True: try: return nonblocking_sock.recvfrom(*pos, **kw) except BlockingIOError: future = Future(loop=loop) loop.add_reader(nonblocking_sock.fileno(), future.set_result, None) try: await future finally: loop.remove_reader(nonblocking_sock.fileno()) I’m not very experienced with asyncio, but I imagine having general-purpose loop.wait_readable(file_descriptor) etc methods would make writing these kind of functions easier. |
|
|
msg260614 - (view) |
Author: Simon Bernier St-Pierre (sbstp) * |
Date: 2016-02-21 15:40 |
I created a patch for it on the asyncio github repo. https://github.com/python/asyncio/pull/321 |
|
|