NAME

Linux::Event::IO::Sock::Dgram - Asynchronous UDP and Unix datagram sockets

SYNOPSIS

use v5.36;
use Linux::Event::Loop;
use Linux::Event::IO::Sock::Dgram;

my $loop = Linux::Event::Loop->new;

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,
    host => '127.0.0.1',
    port => 9999,

    on_datagram => sub ($self, $payload, $peer) {
        say "Received: $payload";

        $self->send(
            "reply",
            to => $peer,
        );
    },
);

$loop->run;

DESCRIPTION

Linux::Event::IO::Sock::Dgram represents a datagram socket.

It supports:

  • UDP over IPv4

  • UDP over IPv6

  • Unix-domain datagram sockets

  • bound sockets that communicate with many peers

  • connected datagram sockets with one default peer

  • already-created datagram sockets adopted from other code

Unlike a Stream, a Datagram preserves packet boundaries.

One received datagram produces one on_datagram callback.

One send call sends one datagram.

Linux::Event does not combine separate packets into a byte stream and does not split one accepted packet into smaller application messages.

DATAGRAMS ARE NOT STREAMS

This distinction is important.

TCP gives an ordered stream of bytes. UDP gives individual packets.

With a Stream, application message boundaries may need to be reconstructed with a Linux::Event::Framer.

With a Datagram, the kernel already preserves the packet boundary:

packet 1
packet 2
packet 3

Each arrives separately.

For that reason, Stream framing and TLS policy do not apply to Datagram objects.

CREATING A BOUND UDP SOCKET

Use new to create a UDP socket that can receive packets from many peers:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,
    host => '0.0.0.0',
    port => 9999,

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

host

host => '0.0.0.0'

The local address to bind.

For example:

127.0.0.1

binds only to the local IPv4 loopback interface, while:

0.0.0.0

requests all IPv4 interfaces.

IPv6 addresses may also be used.

port

port => 9999

The local UDP port.

A value of zero asks the kernel to select an available port.

The selected local address can be inspected through local.

loop

loop => $loop

Attach the Datagram to a Loop immediately.

The option is optional.

A detached Datagram may instead be added later:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    host => '127.0.0.1',
    port => 9999,
    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

$loop->add($socket);

RECEIVING DATAGRAMS

on_datagram

Every Datagram requires an effective on_datagram callback.

It may be supplied directly:

on_datagram => sub ($self, $payload, $peer) {
    ...
}

or implemented by a subclass.

The callback receives:

$socket

The Datagram object.

$payload

The complete packet payload.

$peer

A Linux::Event::Address describing the sender.

For example:

on_datagram => sub ($self, $payload, $peer) {
    say "Received " . length($payload) . " bytes";
    say "From: $peer";
}

Zero-length datagrams are valid and are delivered normally.

REPLYING TO THE SENDER

For an unconnected Datagram, specify the destination with to:

on_datagram => sub ($self, $payload, $peer) {
    $self->send(
        "Thanks",
        to => $peer,
    );
}

The to value must be a Linux::Event::Address.

This makes simple request/reply UDP services straightforward because the peer address received with a packet can be used directly for the reply.

CONNECTED DATAGRAM SOCKETS

Use connect when a Datagram should have one default peer:

my $socket = Linux::Event::IO::Sock::Dgram->connect(
    loop => $loop,
    host => 'collector.example.com',
    port => 9000,

    on_ready => sub ($self) {
        $self->send("hello");
    },

    on_datagram => sub ($self, $payload, $peer) {
        say "Received: $payload";
    },
);

For a connected Datagram:

$socket->send("hello");

does not require to.

What connect means for UDP

UDP connect is not the same kind of connection as TCP connect.

It does not create a reliable byte stream, perform a handshake, or guarantee delivery.

It tells the kernel which peer this datagram socket normally communicates with.

That gives the socket a default destination and allows:

$socket->send($payload);

instead of:

$socket->send($payload, to => $peer);

A connected Datagram rejects to because its destination is already defined.

Hostname resolution

For connected UDP sockets, hostnames are resolved asynchronously.

For example:

host => 'collector.example.com'

may require DNS resolution before the socket becomes ready.

Numeric IP addresses do not require hostname resolution.

on_ready

A connected Datagram may use:

on_ready => sub ($self) {
    $self->send("hello");
}

on_ready is called when the Datagram is active and ready for application use.

This is especially useful when connect required asynchronous hostname resolution.

UNIX-DOMAIN DATAGRAMS

A bound Unix-domain datagram socket uses unix:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,
    unix => '/run/my-service.sock',

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

A connected Unix-domain Datagram also uses unix for its peer:

my $socket = Linux::Event::IO::Sock::Dgram->connect(
    loop => $loop,
    unix => '/run/my-service.sock',

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

A connected Unix-domain Datagram may use local_unix when it needs its own local filesystem path for replies.

SENDING DATAGRAMS

send

For a connected Datagram:

$socket->send($payload);

For an unconnected Datagram:

$socket->send(
    $payload,
    to => $peer,
);

One call to send represents one complete packet.

Linux::Event never intentionally breaks one accepted datagram into several packets.

When the socket would block

If the kernel cannot send the packet immediately, Linux::Event queues the complete datagram and retries it later.

The packet remains a packet.

It is not partially delivered to the application as though it were Stream output.

Return value and backpressure

send normally returns true while output pressure remains below the high watermark.

When queued output grows past the high watermark, the datagram is still accepted, but send begins returning false.

That tells the application to slow down.

When the queue later drains to the low watermark, on_drain is called.

OUTPUT BACKPRESSURE

For example:

my $ok = $socket->send(
    $payload,
    to => $peer,
);

if (!$ok) {
    # Stop producing more packets until on_drain.
}

Then:

on_drain => sub ($self) {
    # Producing more output is safe again.
}

Hard queue limits may also be configured.

If sending another datagram would exceed a hard queue limit, that datagram is not accepted and the error is reported through on_error.

CALLBACKS

new and connect accept these callbacks:

on_datagram($socket, $payload, $peer)

One complete datagram was received.

This callback is required.

on_ready($socket)

The Datagram became active and ready for application use.

on_drain($socket)

Queued output fell to the low watermark after backpressure.

on_error($socket, $error)

An asynchronous socket, receive, send, size, or queue-limit error occurred.

on_close($socket)

The Datagram closed.

Callbacks may also be implemented as subclass methods.

A constructor callback overrides a same-named subclass method for that object.

ERRORS

on_error

on_error => sub ($self, $error) {
    warn "$error\n";
}

$error is a Linux::Event::Error object.

Datagram errors do not invent Stream concepts such as EOF.

A failed packet does not mean there is a byte stream that has ended.

last_error

my $error = $socket->last_error;

Return the most recently stored error, when one exists.

PAUSING INPUT

pause_read

$socket->pause_read;

Temporarily stop receiving application datagrams.

resume_read

$socket->resume_read;

Resume receiving datagrams.

is_read_paused

if ($socket->is_read_paused) {
    ...
}

Return true when Datagram input is currently paused.

SUBCLASSING

Subclassing is optional.

A simple UDP service can use constructor callbacks directly:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    ...
    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

Subclassing is useful when many Datagram objects should share the same callbacks or socket policy.

For example:

package DiscoverySocket;

use parent 'Linux::Event::IO::Sock::Dgram';

sub on_datagram ($self, $payload, $peer) {
    ...
}

sub datagram_options ($class) {
    return (
        max_datagram_size      => 32_768,
        max_datagrams_per_tick => 128,
    );
}

DATAGRAM OPTIONS

Reusable Datagram policy can be declared in a subclass with datagram_options.

For example:

package ServiceDgram;

use parent 'Linux::Event::IO::Sock::Dgram';

sub datagram_options ($class) {
    return (
        max_datagram_size      => 32_768,
        max_datagrams_per_tick => 128,
        receive_buffer         => 1_048_576,
    );
}

Constructor values may override these defaults for one Datagram.

The same options can therefore also be supplied directly at the top level of new or connect:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,
    host => '0.0.0.0',
    port => 9999,

    max_datagram_size      => 32_768,
    max_datagrams_per_tick => 128,
    receive_buffer         => 1_048_576,

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

There is no separate tuning hash for Datagram constructor options.

INPUT SIZE AND FAIRNESS

max_datagram_size

Default: 65,535 bytes.

max_datagram_size => 32_768

Largest packet Linux::Event will accept.

The allowed range is 1 through 16,777,216 bytes.

If a received packet is larger than this limit, Linux::Event rejects the whole packet and reports an error.

It does not deliver a misleading truncated prefix to on_datagram.

The same limit also applies to packets supplied to send.

max_datagrams_per_tick

Default: 256.

max_datagrams_per_tick => 128

Maximum number of received datagrams processed during one readiness turn.

This is a fairness control.

A socket receiving packets continuously should not indefinitely prevent timers, Streams, or other resources from getting a turn.

A value of zero means continue receiving until the socket would block.

edge_triggered

Default: false.

edge_triggered => 1

Use edge-triggered receive readiness.

This is an advanced option.

When enabled, max_datagrams_per_tick must be zero because the socket must be drained until EAGAIN.

OUTPUT QUEUE LIMITS

high_watermark

Default: 1,048,576 bytes.

When queued datagram payload reaches this level, send begins returning false to signal backpressure.

low_watermark

Default: 262,144 bytes.

After high-watermark backpressure has occurred, on_drain fires when queued payload falls to or below this level.

The low watermark cannot exceed the high watermark.

max_pending_bytes

Default: 0.

Hard limit on the total queued datagram payload bytes.

Zero means no hard byte limit.

max_pending_datagrams

Default: 0.

Hard limit on the number of queued datagrams.

Zero means no hard packet-count limit.

COMMON SOCKET OPTIONS

The following settings may be placed in datagram_options or supplied directly to the constructor.

reuseaddr

Default: false.

reuseaddr => 1

Controls SO_REUSEADDR.

reuseport

Default: false.

reuseport => 1

Controls SO_REUSEPORT.

broadcast

Default: false.

broadcast => 1

Controls SO_BROADCAST.

This is used with IPv4 UDP broadcast.

v6only

For IPv6 sockets:

v6only => 1

controls IPV6_V6ONLY.

When unspecified, the operating-system default is used.

send_buffer

send_buffer => 1_048_576

Request a SO_SNDBUF size.

receive_buffer

receive_buffer => 1_048_576

Request a SO_RCVBUF size.

TOP-LEVEL PER-SOCKET CONSTRUCTOR OPTIONS

The options in this section are supplied directly to new() or connect().

They do not go inside datagram_options, and they do not go inside a nested tuning or socket hash.

For example, a connected UDP socket with an explicit local address and network interface can be written as:

my $socket = Linux::Event::IO::Sock::Dgram->connect(
    loop => $loop,

    host => '192.0.2.20',
    port => 9000,

    local_host  => '192.0.2.10',
    local_port  => 0,
    bind_device => 'eth0',

    max_datagram_size => 32_768,

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

Here:

local_host
local_port
bind_device

are per-socket constructor options.

max_datagram_size is also a top-level constructor option in this example, but unlike the three options above it may alternatively be supplied through a subclass's datagram_options policy.

A Unix-domain Datagram provides another example:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,

    unix            => '/run/my-service.sock',
    unlink          => 1,
    unlink_on_close => 1,
    permissions     => 0660,

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

unlink, unlink_on_close, and permissions apply only to that particular Unix-domain Datagram.

The per-socket options include:

bind_device
unlink
unlink_on_close
permissions
owns_socket
local_host
local_port
local_unix

Not every option is valid for every socket source. For example, Unix filesystem options do not apply to an ordinary UDP socket, and local_host does not apply to a Unix-domain Datagram.

bind_device

my $socket = Linux::Event::IO::Sock::Dgram->new(
    host        => '0.0.0.0',
    port        => 9999,
    bind_device => 'eth0',
    ...
);

Bind an Internet datagram socket to a particular Linux interface using SO_BINDTODEVICE.

local_host and local_port

A connected UDP Datagram may request a particular local address or port:

my $socket = Linux::Event::IO::Sock::Dgram->connect(
    host       => '192.0.2.20',
    port       => 9000,
    local_host => '192.0.2.10',
    local_port => 0,
    ...
);

local_unix

A connected Unix-domain Datagram may give itself a local filesystem path:

my $socket = Linux::Event::IO::Sock::Dgram->connect(
    unix       => '/run/server.sock',
    local_unix => '/run/client.sock',
    ...
);

This is useful when the peer needs a filesystem address to which it can send replies.

owns_socket

owns_socket applies when adopting an existing socket with fh:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    fh          => $fh,
    owns_socket => 1,
    ...
);

When true, Linux::Event owns and closes the supplied socket.

UNIX SOCKET PATH OPTIONS

Unix-domain Datagram sockets may use filesystem ownership options.

These are top-level constructor options.

For example:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    unix            => '/run/service.sock',
    unlink          => 1,
    unlink_on_close => 1,
    permissions     => 0660,
    ...
);

Allow an existing Unix socket path to be removed during setup.

Control whether an owned Unix socket path is removed when the Datagram closes.

The default is true.

permissions

Set filesystem permissions for a newly created Unix-domain socket path.

ADOPTING AN EXISTING DATAGRAM SOCKET

new can adopt an existing datagram socket:

my $socket = Linux::Event::IO::Sock::Dgram->new(
    loop => $loop,
    fh   => $fh,

    on_datagram => sub ($self, $payload, $peer) {
        ...
    },
);

The handle must be an IPv4, IPv6, or Unix SOCK_DGRAM socket.

Linux::Event configures adopted handles for nonblocking and close-on-exec use.

By default an adopted socket remains caller-owned.

Use:

owns_socket => 1

when the Datagram object should own and close the supplied socket.

RUNTIME SOCKET SETTINGS

Some socket settings can also be inspected or changed after the Datagram is active.

send_buffer

my $size = $socket->send_buffer;

$socket->send_buffer(1_048_576);

Read or request the active socket's send-buffer size.

receive_buffer

my $size = $socket->receive_buffer;

$socket->receive_buffer(1_048_576);

Read or request the active socket's receive-buffer size.

broadcast

my $enabled = $socket->broadcast;

$socket->broadcast(1);

Read or change IPv4 broadcast permission on the active socket.

ADDRESS INFORMATION

local

my $address = $socket->local;

Return the local Linux::Event::Address when available.

peer

my $address = $socket->peer;

Return the default peer for a connected Datagram.

For an unconnected Datagram there may be no single peer because packets can arrive from many different addresses.

is_connected

if ($socket->is_connected) {
    ...
}

Return true when the Datagram has a default connected peer.

QUEUE INFORMATION

pending_bytes

my $bytes = $socket->pending_bytes;

Return the number of datagram payload bytes currently queued for output.

pending_datagrams

my $count = $socket->pending_datagrams;

Return the number of complete datagrams currently queued for output.

OTHER INFORMATION METHODS

fd

Return the active socket's integer file descriptor when available.

fh

Return the active Perl socket handle when available.

loop

Return the owning Linux::Event::Loop while attached.

state

Return the current lifecycle state.

data

Store or retrieve arbitrary application state:

$socket->data($value);

my $value = $socket->data;

is_active

Return true while the Datagram is active.

CLOSING AND DETACHING

close

$socket->close;

Close the Datagram and release resources it owns.

close is terminal.

on_close

on_close => sub ($self) {
    ...
}

Called when the Datagram closes through its normal lifecycle.

detach

my $fh = $socket->detach;

Remove the socket from Linux::Event and return the still-open handle.

Detachment is terminal for the Datagram object.

Linux::Event gives up ownership of the handle and suppresses removal of an owned Unix socket path.

PERFORMANCE MODEL

Datagram policy and callbacks are resolved when the object is constructed.

Normal packet delivery does not repeatedly perform method lookup or rebuild socket policy.

Packet receiving is handled natively, including detection of oversized packets without passing truncated data to Perl.

These details normally require no application action.

SEE ALSO

Linux::Event, Linux::Event::Loop, Linux::Event::IO::Sock::Stream, Linux::Event::Address, Linux::Event::Error, docs/DGRAM-DESIGN.md, docs/SOCKET-CONFIGURATION.md.