# flare.udp.socket

> A module of flare_mlake's API.

UDP socket: connectionless datagrams.

``UdpSocket`` wraps a ``RawSocket`` in ``SOCK_DGRAM`` mode and exposes
``send_to`` / ``recv_from`` operations. Use ``bind()`` for a receiving
socket and ``unbound()`` for a send-only socket.

Platform notes:
- Maximum UDP payload: 65507 bytes (65535 − 8-byte UDP header − 20-byte
  IP header). Larger datagrams must be rejected by the caller.
- ``SO_BROADCAST`` must be explicitly enabled to send to broadcast
  addresses (e.g. ``255.255.255.255``).

## Structs

### `DatagramTooLarge`

```mojo
struct DatagramTooLarge
```

Raised when a UDP datagram exceeds the maximum allowed payload size.

Fields:
    size: The datagram size in bytes that was rejected.
    max_size: The platform maximum (``65507`` bytes).

Example:
```mojo
raise DatagramTooLarge(70000)
```

Implements: `Copyable`, `Movable`, `Writable`

Fields:

- `size: Int`
- `max_size: Int`

#### `__init__`

```mojo
def __init__(out self, size: Int, max_size: Int = Int(65507))
```

Initialise a DatagramTooLarge error.

Args:

- `size`: The requested datagram size.
- `max_size`: The allowed maximum. Defaults to ``65507``.

#### `write_to`

```mojo
def write_to[W: Writer](self, mut writer: W)
```

Write the error description to ``writer``.

Args:

- `writer`: Destination writer.

### `UdpSocket`

```mojo
struct UdpSocket
```

A UDP socket for sending and receiving datagrams.

``UdpSocket`` is ``Movable`` but not ``Copyable`` — a socket file
descriptor cannot be shared without tracking both copies.

Use ``bind()`` to receive datagrams from any sender, or ``unbound()``
to create a send-only socket that the OS assigns a source port to.

Thread safety:
    Not thread-safe .

Example:
```mojo
from flare.udp import UdpSocket
from flare.net import SocketAddr

# Receiver
var recv = UdpSocket.bind(SocketAddr.localhost(5000))
var buf = List[UInt8]()
buf.resize(1024, 0)
var (n, from_addr) = recv.recv_from(Span[UInt8, _](buf))
print("received", n, "bytes from", String(from_addr))

# Sender
var sender = UdpSocket.unbound()
sender.send_to("hello".as_bytes(), SocketAddr.localhost(5000))
```

Implements: `Movable`

#### `__init__`

```mojo
def __init__(out self, var socket: RawSocket, local: SocketAddr)
```

Wrap an existing ``RawSocket`` (internal use only).

Safety:
The caller must not use ``socket`` after this constructor.

Args:

- `socket`: An open UDP socket (ownership transferred).
- `local`: The bound address; use ``SocketAddr(IpAddr.unspecified(), 0)``
        for an unbound socket.

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

#### `bind`

```mojo
def bind(addr: SocketAddr, reuse_port: Bool = False) -> Self
```

Create a UDP socket bound to ``addr``.

After this call, ``recv_from()`` will receive datagrams sent to
the bound address.

Example:
```mojo
var s = UdpSocket.bind(SocketAddr.localhost(5000))
```

Args:

- `addr`: The local address to bind. Use port ``0`` for an
      OS-assigned ephemeral port.
- `reuse_port`: Set ``SO_REUSEPORT`` first, so several sockets
      of this user can bind the port and the kernel spreads
      flows across them by 4-tuple (a multi-worker server).

Raises: AddressInUse: If the port is already in use.
NetworkError: For any other OS bind error.

#### `unbound`

```mojo
def unbound(ipv6: Bool = False) -> Self
```

Create a send-only UDP socket without binding to a specific port.

The OS assigns an ephemeral source port on the first ``send_to()``.
``ipv6`` picks an AF_INET6 socket, needed to send to IPv6
destinations.

Example:
```mojo
var s = UdpSocket.unbound()
var n = s.send_to("hello".as_bytes(), SocketAddr.localhost(5000))
```

Raises: NetworkError: If ``socket(2)`` fails.

#### `__enter__`

```mojo
def __enter__(var self) -> Self
```

Transfer ownership of ``self`` into the ``with`` block.

#### `send_to`

```mojo
def send_to(self, data: Span[UInt8], addr: SocketAddr) -> Int
```

Send a datagram to ``addr``.

Example:
```mojo
var n = s.send_to("hello".as_bytes(), SocketAddr.localhost(9000))
```

Args:

- `data`: The payload bytes to send.
- `addr`: The destination socket address.

Raises: DatagramTooLarge: If ``len(data) > 65507``.
NetworkError: For any OS send error.

#### `try_recv_from`

```mojo
def try_recv_from(mut self, buf: Span[UInt8]) -> Tuple[Int, SocketAddr]
```

Non-blocking ``recv_from`` via ``MSG_DONTWAIT``.

Identical to :meth:`recv_from` but never blocks: raises
:class:`Timeout` immediately when the socket queue is empty
(``EAGAIN`` / ``EWOULDBLOCK``). Used to drain a burst of
queued datagrams in one reactor tick without touching
``SO_RCVTIMEO`` per call.

#### `recv_from`

```mojo
def recv_from(mut self, buf: Span[UInt8]) -> Tuple[Int, SocketAddr]
```

Receive a datagram, returning the byte count and sender address.

Blocks until a datagram arrives or a timeout expires.

Example:
```mojo
var buf = List[UInt8]()
buf.resize(1024, 0)
var (n, sender) = s.recv_from(Span[UInt8, _](buf))
print("got", n, "bytes from", String(sender))
```

Args:

- `buf`: Destination span; at most ``len(buf)`` bytes are written.
     For UDP, this should be large enough for the expected
     datagram (up to 65507 bytes).

Raises: Timeout: If a recv timeout was set and expired.
NetworkError: For any other OS error.

#### `set_recv_timeout`

```mojo
def set_recv_timeout(self, ms: Int)
```

Set a receive timeout via ``SO_RCVTIMEO``.

After the timeout expires, ``recv_from()`` raises ``Timeout``.

Args:

- `ms`: Timeout in milliseconds. 0 disables the timeout.

Raises: NetworkError: If ``setsockopt(2)`` fails.

#### `set_recv_buffer`

```mojo
def set_recv_buffer(self, bytes: Int)
```

Request a kernel receive-buffer size via ``SO_RCVBUF``.

The kernel doubles the request and clamps it to
``net.core.rmem_max``; the effective size may be smaller. A
multi-MB buffer absorbs datagram bursts so the kernel does not
drop packets between reactor ticks under load.

Args:

- `bytes`: Requested receive-buffer size in bytes.

Raises: NetworkError: If ``setsockopt(2)`` fails.

#### `set_send_buffer`

```mojo
def set_send_buffer(self, bytes: Int)
```

Request a kernel send-buffer size via ``SO_SNDBUF``.

Args:

- `bytes`: Requested send-buffer size in bytes.

Raises: NetworkError: If ``setsockopt(2)`` fails.

#### `set_ecn`

```mojo
def set_ecn(self, codepoint: Int)
```

Mark outgoing datagrams with the ECN ``codepoint`` (0 none, 2 ECT(0), 1 ECT(1), 3 CE; RFC 3168) and deliver received datagrams' codepoints to :meth:`flare.udp.BatchReceiver.ecn`. Linux only; a no-op elsewhere.

Raises: NetworkError: If ``setsockopt(2)`` fails.

#### `set_broadcast`

```mojo
def set_broadcast(self, enabled: Bool)
```

Enable or disable sending to broadcast addresses.

Must be enabled before ``send_to()`` can send to ``255.255.255.255``
or a subnet broadcast (e.g. ``192.168.1.255``).

Args:

- `enabled`: ``True`` to allow broadcast sends.

Raises: NetworkError: If ``setsockopt(2)`` fails.

#### `local_addr`

```mojo
def local_addr(self) -> SocketAddr
```

Return the local address the socket is bound to.

#### `fd`

```mojo
def fd(self) -> Int
```

Return the underlying socket file descriptor.

Exposed for the batched-I/O helpers in ``flare.udp.batch``
(``recvmmsg`` / ``sendmmsg`` / GSO) which operate on the raw
fd. Do not close it directly -- ``UdpSocket`` owns its
lifetime.

#### `close`

```mojo
def close(mut self)
```

Close the socket. Idempotent.

## Aliases

### `UDP_MAX_PAYLOAD`

```mojo
comptime UDP_MAX_PAYLOAD = Int(65507)
```
