Skip to content

Datagrams

transport, protocol = await loop.create_datagram_endpoint(
    Protocol, local_addr=("127.0.0.1", 9999)
)

create_datagram_endpoint is implemented natively over uv_udp_t, and takes the full asyncio signature: local_addr, remote_addr, family, proto, flags, reuse_port, allow_broadcast and sock.

The returned transport is an asyncio.DatagramTransport and hands out a real TransportSocket through get_extra_info("socket").

Sending

transport.sendto(b"payload", ("127.0.0.1", 9999))  # unconnected
transport.sendto(b"payload")                       # connected, via remote_addr

An endpoint is connected if it was created with remote_addr, or from a sock that was already connected. sendto() then needs no address, and accepts one only if it names the peer connected to - any other raises ValueError. An unconnected endpoint requires an address on every send.

A datagram the kernel accepts outright goes out through uv_udp_try_send without allocating a request.

Errors

A failed send does not raise. asyncio's contract is that the endpoint stays usable, so the error reaches your protocol:

class Protocol(asyncio.DatagramProtocol):
    def error_received(self, exc: Exception) -> None: ...

A datagram too large for the receive buffer is reported the same way rather than delivered truncated. A silent prefix is worse than a reported loss.

Unix datagrams

transport, protocol = await loop.create_datagram_endpoint(
    Protocol, local_addr="/tmp/server.sock", family=socket.AF_UNIX
)