NAME

Linux::Event::Datagram - connected and unconnected packet sockets

SYNOPSIS

package LE::Discovery;
use parent 'Linux::Event::Datagram';

sub on_datagram ($socket, $payload, $peer) {
    $socket->send($payload, to => $peer); # required when unconnected
}

package main;
my $socket = $loop->add(LE::Discovery->new(
    host => '0.0.0.0', # required for UDP bind
    port => 9999,      # required for UDP bind
));

DESCRIPTION

Datagram preserves packet boundaries and peer addresses for UDP and Unix datagram sockets. A concrete subclass defines one named on_datagram method. Connected and unconnected endpoints share one lifecycle and native packet I/O engine; Datagram does not reuse byte-stream buffering or framing semantics.

An Internet or Unix server socket is created and bound during new. It starts in unattached state until added to a Loop. A connected socket is created when connect attaches, so hostname resolution and connection failure are reported through the asynchronous lifecycle. Output queued before attachment is retained whole.

UNCONNECTED UDP

my $socket = LE::Discovery->new(
    loop => $loop,     # optional: attach immediately
    host => '0.0.0.0', # required
    port => 9999,      # required; 0 selects an ephemeral port
    reuseaddr => 0,    # default
    reuseport => 0,    # default
    broadcast => 0,    # default
);

Send each packet to an explicit Linux::Event::Address:

$socket->send($payload, to => $peer); # required when unconnected

CONNECTED UDP

my $socket = LE::Telemetry->connect(
    loop       => $loop,                  # optional
    host       => 'collector.example.com', # required
    port       => 9000,                   # required
    local_host => '0.0.0.0',              # optional
    local_port => 0,                      # optional; default 0
);

Hostname resolution uses the Loop's native resolver workers. on_ready runs on a later Loop turn after the socket is available. UDP connect stores a default peer and filters input; it does not establish a network session.

local_host accepts a numeric IPv4 or IPv6 address only. It never starts a second DNS lookup. local_port without local_host binds the matching wildcard family. An incompatible local and peer family produces a structured socket_configuration error.

Send without a destination:

$socket->send($payload);

UNIX DATAGRAMS

my $server = LE::LocalServer->new(
    unix            => '/run/app.sock', # required
    unlink          => 0,               # default
    unlink_on_close => 1,               # default
    permissions     => 0660,            # optional
);

my $client = LE::LocalClient->connect(
    unix       => '/run/app.sock',        # required peer path
    local_unix => '/run/app-client.sock', # optional reply path
    unlink     => 0,                      # default
    unlink_on_close => 1,                 # default
    permissions => 0600,                  # optional
);

ADOPTED SOCKETS

my $socket = LE::Existing->new(
    fh          => $fh, # required
    owns_socket => 0,   # default
);

Datagram sets nonblocking and close-on-exec flags and detects whether the socket has a default peer. owns_socket => 1 transfers close ownership.

CLASS POLICY

sub datagram_options ($class) {
    return (
        max_datagram_size      => 65_535,    # default
        max_datagrams_per_tick => 256,       # default
        edge_triggered         => 0,         # default
        high_watermark         => 1_048_576, # default
        low_watermark          => 262_144,   # default
        max_pending_bytes      => 0,         # default: unlimited
        max_pending_datagrams  => 0,         # default: unlimited
        reuseaddr              => 0,         # default
        reuseport              => 0,         # default
        broadcast              => 0,         # default
        v6only                 => 1,         # optional
        send_buffer            => 262_144,   # optional; bytes
        receive_buffer         => 262_144,   # optional; bytes
    );
}

The method runs once per concrete subclass. Constructor values override class policy. An omitted optional socket setting leaves the kernel setting unchanged. edge_triggered => 1 requires max_datagrams_per_tick => 0 so readiness always drains to EAGAIN.

reuseaddr, reuseport, broadcast, v6only, and bind_device apply only to Internet sockets. broadcast is IPv4-only and v6only is IPv6-only. unlink, unlink_on_close, and permissions apply only to created Unix paths. Source-specific options are rejected even when explicitly set to a false value, preventing configuration that appears accepted but has no effect. Send and receive buffers apply to Internet, Unix, and adopted sockets.

CALLBACKS

sub on_datagram ($socket, $payload, $peer) { # required
    $socket->is_connected
        ? $socket->send("ack:$payload")
        : $socket->send("ack:$payload", to => $peer); # required unconnected
}

sub on_drain ($socket) {                    # optional
    $socket->data->{blocked} = 0 if $socket->data;
}

sub on_ready ($socket) {                    # optional
    $socket->send('started') if $socket->is_connected;
}

sub on_error ($socket, $error) {            # optional
    warn "$error\n";
}

sub on_close ($socket) {                    # optional
    $socket->data->{closed} = 1 if $socket->data;
}

$peer is always an Address. Callback CVs are cached per subclass. Callback exceptions propagate from Loop dispatch.

An advanced subclass may customize a newly acquired socket:

use Socket qw(SOL_SOCKET SO_RCVBUF);

sub configure_socket ($socket, $fh, $role, $address) {
    setsockopt($fh, SOL_SOCKET, SO_RCVBUF, pack('i', 524_288))
        or die "setsockopt(SO_RCVBUF): $!";
}

$role is bind, connect, or adopted. For bind, $address is the effective local Address and the hook runs after the socket has been bound. Built-in policy is applied before this hook and before a connected socket performs local binding or connect.

on_error receives connection, packet-I/O, queue-limit, truncation, and socket-configuration failures. A terminal connection failure has already released native resources, but $socket->loop remains available during the callback and is released when it returns. Without on_error, Datagram warns and retains the failure in last_error.

OUTPUT AND BACKPRESSURE

send represents exactly one packet. A packet that would block is queued whole. The return is false when accepted output is above the soft high watermark; on_drain runs after it reaches the low watermark. A hard byte or packet limit rejects only the new packet and reports an output_limit error.

Input larger than max_datagram_size is detected with MSG_TRUNC, discarded whole, and reported as datagram_size. Partial packets are never delivered.

METHODS

send($payload) / send($payload, to => $address)

Send on a connected endpoint or to an explicit peer on an unconnected one. The payload must be a byte string.

local / peer

Return the local Address and the connected peer. peer is undef for an unconnected endpoint.

pause_read / resume_read

Disable or re-enable input readiness without closing the socket.

send_buffer([$bytes]) / receive_buffer([$bytes]) / broadcast([$boolean])

Get or live-set the effective Linux socket value. Linux may round or double buffer requests, so buffer setters return the value read back from the kernel. broadcast is available only on IPv4 sockets.

pending_bytes / pending_datagrams

Return queued output totals.

close / detach

close ends ownership. detach cancels readiness and returns the still-open handle without unlinking a Unix path or calling on_close. on_close runs for explicit close while the Loop remains available, then the object releases its Loop.

data([$value]) / loop / fh / fd / state / last_error

Return or update ordinary instance and lifecycle information.

is_connected / is_active / is_terminal / is_read_paused

Report socket and lifecycle categories.