3

I have a socket object created with socket(AF_INET, SOCK_DGRAM) which I will be using it in an asyncio loop. However I couldn't find sendto function in https://docs.python.org/3/library/asyncio-eventloop.html#low-level-socket-operations.

Can I safely assume that this function is a non-blocking system call which can be invoked inside an asyncio loop? Or should I submit it to run in another thread with run_in_executor?

** Documentation states that it performs a system call which concerned me that it might block the entire loop.

SpringMaple
  • 311
  • 3
  • 11
  • [2432814](http://stackoverflow.com/questions/2432814/python-blocking-sockets-send-returns-immediately) duplicate? – ldavid Oct 17 '16 at 12:34
  • 1
    sorry for missing out that post with `search Q&A`. I was searching with keywords like "is sendto non-blocking", "is sendto safe to use in asyncio loop".. etc – SpringMaple Oct 18 '16 at 03:19

2 Answers2

4

No, you cannot expect socket.sendto to be non-blocking.

Instead, use DatagramTransport.sendto:

Send the data bytes to the remote peer given by addr (a transport-> dependent target address). If addr is None, the data is sent to the target address given on transport creation.

This method does not block; it buffers the data and arranges for it to be sent out asynchronously.

The datagram transport is returned by the loop.create_datagram_endpoint coroutine:

transport, protocol = await loop.create_datagram_endpoint(factory, sock=sock)

EDIT - About your comment:

Is socket.sendto() equivalent to transport.sendto()?

No it's not, transport.sendto uses loop.add_writer to make the operation non-blocking. See the implementation.

I do not want to use this method because of it's implementation which enforce me to receive data through protocol with callback style.

The lower level of asyncio is based on callbacks and asyncio doesn't provide coroutine-based objects for UDP. However, I wrote a module that provides high-level UDP endpoints for asyncio.

Usage:

async def main():
    local = await open_local_endpoint()
    remote = await open_remote_endpoint(*local.address)
    remote.write(b'Hey Hey, My My')
    data, addr = await local.read()
    message = "Got {data!r} from {addr[0]} port {addr[1]}"
    print(message.format(data=data.decode(), addr=addr))

Output:

Got 'Hey Hey, My My' from 127.0.0.1 port 45551
Community
  • 1
  • 1
Vincent
  • 12,919
  • 1
  • 42
  • 64
  • Is socket.sendto() equivalent to transport.sendto()? I do not want to use this method because of it's implementation which enforce me to receive data through `protocol` with callback style. I would like to do [sendto ... recv ... recv ... (loop) then do something ,,,] all in one function rather than scattered around. BTW I will accept this as a solution, thanks! – SpringMaple Oct 18 '16 at 03:17
  • Thank you, your module helped me alot. – SpringMaple Oct 18 '16 at 11:57
-2

sendto() is non-blocking, and might raise (BlockingIOError, InterruptedError) if writer is currently unavailable. Difference between socket.sendto() and transport.sendto() is that transport.sendto() will attempt to call socket.sendto() first, or wait until socket is ready to write with loop.add_writer() and call socket.sendto() again if failed to send on first attempt.

This is what I observed from the source code in the asyncio module for Python 3.5.2 (Windows 32bit)

EDIT:

In Windows, socket operation blocking behaviour is specified by socket.settimeout, so make sure to set timeout value to 0 so that its operations do not block.

SpringMaple
  • 311
  • 3
  • 11