silk.net.datagram_native.

Nonblocking UDP sockets on the native system: send and receive whole datagrams.

When to use#

Use a NativeUdpSocket to send datagrams, such as DNS queries, from a native program. Every socket is nonblocking: a send or a receive that cannot complete now returns WouldBlock and does not wait. Call NativeUdpSocket.waitReadable to wait, or register NativeUdpSocket.rawDescriptor with your own event loop.

Details#

A socket is IPv4 or IPv6, and is closed when it is dropped. A socket made for IPv6 does not accept IPv4 traffic. After NativeUdpSocket.connect, the system drops datagrams from other peers, and reports ConnectionRefused when the peer reports that its port is closed. waitAny waits for any of several sockets.

The module supports Linux with GNU libc, macOS, and Windows on x86_64.

Examples#

Send a datagram to yourself#

silk
import silk.core.effect { Effect }
import silk.net.address { Endpoint, IpAddress, Ipv4Address, Port }
import silk.net.datagram_native { DatagramError, Family, NativeUdpSocket, Received, Sent }
import silk.system.monotonic_clock { MonotonicClock }
import silk.system.monotonic_clock_native { OsMonotonicClock }

effect fn program() -> i32 ! DatagramError ? &mut MonotonicClock {
  let loopback = IpAddress.V4 { value: Ipv4Address.fromOctets([127, 0, 0, 1]) }
  let mut socket = run NativeUdpSocket.open(Family.V4)
  run NativeUdpSocket.bind(&mut socket, Endpoint.make(loopback, Port.fromU16(0)))
  let local = run NativeUdpSocket.localEndpoint(&socket)
  match run NativeUdpSocket.sendTo(&mut socket, b"ping", local) {
    Sent.Count { count } => {
      if count != 4 { return 1 }
    }
    Sent.WouldBlock => { return 1 }
  }
  let now = run MonotonicClock.now()
  if !(run NativeUdpSocket.waitReadable(&socket, MonotonicClock.deadlineAfter(&now, 1000000000))) {
    return 1
  }
  let mut buffer: [u8; 16] = [0; 16]
  return match run NativeUdpSocket.receive(&mut socket, &mut buffer) {
    Received.Datagram { count, from } => {
      if count != 4 || buffer[0] != 112 || !Endpoint.equals(&from, &local) { return 1 }
      return 0
    }
    Received.WouldBlock => 1
  }
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

pub fn main() -> i32 {
  let mut clock = OsMonotonicClock.make()
  return run Effect.catchAll(program(), failed) |> Effect.provideMut<MonotonicClock>(&mut clock)
}

Import as NativeUdpSocket with import silk.net.datagram_native { NativeUdpSocket }.

Public declarations: 9.

RawSocket#

silk
pub type RawSocket = i32

The type of a raw socket handle: a file descriptor.

Family#

silk
pub enum Family

The address family of a socket.

V4#

silk
V4

IPv4.

V6#

silk
V6

IPv6.

MulticastInterface#

silk
pub union MulticastInterface

The network interface for multicast traffic, in NativeUdpSocket.setMulticastInterface and the group calls such as NativeUdpSocket.joinGroup.

Default#

silk
Default

The interface that the system chooses.

V4#

silk
V4

The IPv4 interface with this local address. Use it with IPv4 groups.

Field address#

silk
pub address: Ipv4Address

The local address of the interface.

V6#

silk
V6

The IPv6 interface with this index, as if_nametoindex returns it. Use it with IPv6 groups.

Field index#

silk
pub index: u32

The index of the interface.

Implementation Copy for MulticastInterface#

silk
impl Copy for MulticastInterface

DatagramOperation#

silk
pub enum DatagramOperation

The system call that failed.

Socket#

silk
Socket

Making the socket.

Configure#

silk
Configure

Making the socket nonblocking and not inherited by child processes.

Bind#

silk
Bind

Binding a local address.

Connect#

silk
Connect

Connecting to a peer.

Send#

silk
Send

Sending a datagram.

Receive#

silk
Receive

Receiving a datagram.

LocalAddress#

silk
LocalAddress

Reading the local address.

PeerAddress#

silk
PeerAddress

Reading the address of the connected peer.

Disconnect#

silk
Disconnect

Removing the connected peer.

Option#

silk
Option

Reading or setting a socket option, such as the TTL or a multicast group.

Wait#

silk
Wait

Waiting for a datagram.

Implementation Debug for DatagramOperation#

silk
impl Debug for DatagramOperation

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the name of the variant, such as Send.

DatagramError#

silk
pub union DatagramError

The error from a UDP socket operation.

ConnectionRefused#

silk
ConnectionRefused

The peer reported that its port is closed. On Linux and macOS only a connected socket gets this; on Windows any socket can.

Field operation#

silk
pub operation: DatagramOperation

The operation that saw the report.

AddressInUse#

silk
AddressInUse

The local address is in use.

PermissionDenied#

silk
PermissionDenied

The system does not allow the operation, for example a bind to a privileged port.

NetworkUnreachable#

silk
NetworkUnreachable

The network or host of the peer cannot be reached.

MessageTooLarge#

silk
MessageTooLarge

The datagram is too long to send, or, on Windows, too long for the receive buffer.

FamilyUnsupported#

silk
FamilyUnsupported

The address family is not supported or not enabled.

InvalidNativeAddress#

silk
InvalidNativeAddress

The system returned an address that is not IPv4 or IPv6.

NativeFailure#

silk
NativeFailure

Another system error.

Field operation#

silk
pub operation: DatagramOperation

The operation that failed.

Field code#

silk
pub code: i32

The errno value, or the Winsock error code on Windows.

Implementation Debug for DatagramError#

silk
impl Debug for DatagramError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> ()
    ! WriterError
    ? &mut Writer

Writes the name of the variant and its fields, such as NativeFailure { ... }.

Sent#

silk
pub union Sent

The result of a send.

Count#

silk
Count

The system took the whole datagram.

Field count#

silk
pub count: usize

The number of bytes sent, which is the length of the datagram.

WouldBlock#

silk
WouldBlock

The send buffer is full. Try again later.

Received#

silk
pub union Received

The result of a receive.

Datagram#

silk
Datagram

One datagram is at the start of the buffer.

Field count#

silk
pub count: usize

The number of bytes of the datagram in the buffer. A longer datagram is cut to the buffer length on Linux and macOS.

Field from#

silk
pub from: Endpoint

The address that sent the datagram.

WouldBlock#

silk
WouldBlock

No datagram is waiting. Try again later.

NativeUdpSocket#

silk
pub struct NativeUdpSocket

A nonblocking UDP socket. It is closed when it is dropped.

Implementation Drop for NativeUdpSocket#

silk
impl Drop for NativeUdpSocket

Operation drop#

silk
fn drop(self: &mut NativeUdpSocket) -> ()

Implementation NativeUdpSocket#

silk
impl NativeUdpSocket

Associated function open#

silk
pub effect fn open(family: Family) -> NativeUdpSocket
    ! DatagramError

Makes an unbound UDP socket for family. It is nonblocking, and child processes do not get it.

Errors#

  • FamilyUnsupported if the system has no support for family.

  • Another DatagramError if the system cannot make the socket.

Associated function adopt#

silk
pub unsafe effect fn adopt(fd: RawSocket, family: Family) -> NativeUdpSocket
    ! DatagramError

Takes an open UDP socket of family, such as one that another process sent, and makes it nonblocking and kept from child processes. The returned socket owns fd and closes it when it is dropped.

When to use#

Use it for a socket descriptor that this program did not make, as Node's dgram does for a socket shared by a cluster primary. Use NativeUdpSocket.open for a new socket.

Safety#

fd must be an open UDP socket of family that nothing else closes.

Errors#

  • A DatagramError with operation Configure if the system cannot set the socket flags. The socket is closed then.

Method family#

silk
pub fn family(self: &Self) -> Family

Returns the family of the socket.

Method rawDescriptor#

silk
pub fn rawDescriptor(self: &Self) -> RawSocket

Returns the raw handle of the socket, for example to register it with an event loop. The socket still owns it: do not close it.

Method bind#

silk
pub effect fn bind(self: &mut Self, endpoint: Endpoint) -> ()
    ! DatagramError

Binds the socket to the local address endpoint. Port 0 lets the system choose a free port.

Errors#

  • AddressInUse, PermissionDenied, or another DatagramError from the system.

  • FamilyUnsupported if endpoint is not of the family of the socket.

Method connect#

silk
pub effect fn connect(self: &mut Self, endpoint: Endpoint) -> ()
    ! DatagramError

Connects the socket to endpoint: NativeUdpSocket.send then goes there, and the system drops datagrams from other addresses.

Errors#

  • NetworkUnreachable, or another DatagramError from the system.

  • FamilyUnsupported if endpoint is not of the family of the socket.

Method localEndpoint#

silk
pub effect fn localEndpoint(self: &Self) -> Endpoint
    ! DatagramError

Returns the local address of the socket, such as the port that the system chose.

Errors#

  • A DatagramError from the system.

Method peerEndpoint#

silk
pub effect fn peerEndpoint(self: &Self) -> Endpoint
    ! DatagramError

Returns the address of the peer that NativeUdpSocket.connect set.

Errors#

  • A DatagramError from the system, such as NativeFailure with operation PeerAddress and ENOTCONN when the socket is not connected.

Method disconnect#

silk
pub effect fn disconnect(self: &mut Self) -> ()
    ! DatagramError

Removes the peer that NativeUdpSocket.connect set, so that the socket can send to and receive from any address again. The socket stays bound to its local address.

Errors#

  • A DatagramError from the system.

Method setIpv6Only#

silk
pub effect fn setIpv6Only(self: &mut Self, enabled: bool) -> ()
    ! DatagramError

Sets whether an IPv6 socket takes only IPv6 traffic (IPV6_V6ONLY). NativeUdpSocket.open turns it on. Turn it off before NativeUdpSocket.bind to take IPv4 traffic too, as IPv4-mapped IPv6 addresses.

Errors#

  • FamilyUnsupported for an IPv4 socket.

  • A DatagramError from the system.

Method setReuseAddress#

silk
pub effect fn setReuseAddress(self: &mut Self, enabled: bool) -> ()
    ! DatagramError

Sets whether other sockets can bind the same address and port, as several receivers of one multicast group do. Call it before NativeUdpSocket.bind.

Details#

It sets SO_REUSEADDR. On macOS it also sets SO_REUSEPORT, which macOS needs to share a port.

Errors#

  • A DatagramError from the system.

Method setBroadcast#

silk
pub effect fn setBroadcast(self: &mut Self, enabled: bool) -> ()
    ! DatagramError

Sets whether the socket can send to broadcast addresses (SO_BROADCAST).

Errors#

  • A DatagramError from the system.

Method setTtl#

silk
pub effect fn setTtl(self: &mut Self, hops: u8) -> ()
    ! DatagramError

Sets the hop limit of unicast datagrams: IP_TTL for an IPv4 socket and IPV6_UNICAST_HOPS for an IPv6 socket.

Errors#

  • A DatagramError from the system, such as NativeFailure with EINVAL for 0 on some systems.

Method setMulticastTtl#

silk
pub effect fn setMulticastTtl(self: &mut Self, hops: u8) -> ()
    ! DatagramError

Sets the hop limit of multicast datagrams: IP_MULTICAST_TTL for an IPv4 socket and IPV6_MULTICAST_HOPS for an IPv6 socket. 0 keeps them on this host.

Errors#

  • A DatagramError from the system.

Method setMulticastLoopback#

silk
pub effect fn setMulticastLoopback(self: &mut Self, enabled: bool) -> ()
    ! DatagramError

Sets whether this host gets the multicast datagrams that the socket sends: IP_MULTICAST_LOOP or IPV6_MULTICAST_LOOP.

Errors#

  • A DatagramError from the system.

Method setMulticastInterface#

silk
pub effect fn setMulticastInterface(self: &mut Self, via: MulticastInterface) -> ()
    ! DatagramError

Sets the interface that sends the multicast datagrams of the socket: IP_MULTICAST_IF or IPV6_MULTICAST_IF. Default lets the system choose again.

Errors#

  • FamilyUnsupported if via is V4 for an IPv6 socket, or V6 for an IPv4 socket.

  • A DatagramError from the system, such as NativeFailure with EADDRNOTAVAIL when no interface has the address.

Method joinGroup#

silk
pub effect fn joinGroup(self: &mut Self, group: IpAddress, via: MulticastInterface) -> ()
    ! DatagramError

Joins the multicast group group on via, so that the socket gets its datagrams.

Details#

The family of group chooses the option: IP_ADD_MEMBERSHIP for IPv4 and IPV6_JOIN_GROUP for IPv6. Bind the socket to the port of the group first.

Errors#

  • FamilyUnsupported if via is of the other family than group.

  • A DatagramError from the system, such as NativeFailure with EINVAL when group is not a multicast address, or EADDRINUSE when the socket is already a member.

Method leaveGroup#

silk
pub effect fn leaveGroup(self: &mut Self, group: IpAddress, via: MulticastInterface) -> ()
    ! DatagramError

Leaves the multicast group group on via, which NativeUdpSocket.joinGroup joined.

Errors#

  • FamilyUnsupported if via is of the other family than group.

  • A DatagramError from the system, such as NativeFailure with EADDRNOTAVAIL when the socket is not a member.

Method joinSourceGroup#

silk
pub effect fn joinSourceGroup(
    self: &mut Self,
    group: IpAddress,
    source: IpAddress,
    via: MulticastInterface,
  ) -> ()
    ! DatagramError

Joins the multicast group group on via for datagrams from source only (source-specific multicast).

Details#

IPv4 uses IP_ADD_SOURCE_MEMBERSHIP. IPv6 uses MCAST_JOIN_SOURCE_GROUP.

Errors#

  • FamilyUnsupported if group, source, and via are not all of one family.

  • A DatagramError from the system.

Method leaveSourceGroup#

silk
pub effect fn leaveSourceGroup(
    self: &mut Self,
    group: IpAddress,
    source: IpAddress,
    via: MulticastInterface,
  ) -> ()
    ! DatagramError

Leaves the source-specific group that NativeUdpSocket.joinSourceGroup joined.

Errors#

  • FamilyUnsupported if group, source, and via are not all of one family.

  • A DatagramError from the system.

Method receiveBufferSize#

silk
pub effect fn receiveBufferSize(self: &Self) -> usize
    ! DatagramError

Returns the size of the receive buffer of the socket in bytes (SO_RCVBUF).

Details#

Linux reports twice the size that NativeUdpSocket.setReceiveBufferSize set, because it counts its bookkeeping.

Errors#

  • A DatagramError from the system.

Method setReceiveBufferSize#

silk
pub effect fn setReceiveBufferSize(self: &mut Self, size: usize) -> ()
    ! DatagramError

Asks the system for a receive buffer of size bytes (SO_RCVBUF). The system can round or cap it.

Errors#

  • NativeFailure with operation Option and EINVAL if size does not fit in an int.

  • Another DatagramError from the system.

Method sendBufferSize#

silk
pub effect fn sendBufferSize(self: &Self) -> usize
    ! DatagramError

Returns the size of the send buffer of the socket in bytes (SO_SNDBUF).

Details#

Linux reports twice the size that NativeUdpSocket.setSendBufferSize set, because it counts its bookkeeping.

Errors#

  • A DatagramError from the system.

Method setSendBufferSize#

silk
pub effect fn setSendBufferSize(self: &mut Self, size: usize) -> ()
    ! DatagramError

Asks the system for a send buffer of size bytes (SO_SNDBUF). The system can round or cap it.

Errors#

  • NativeFailure with operation Option and EINVAL if size does not fit in an int.

  • Another DatagramError from the system.

Method send#

silk
pub effect fn send(self: &mut Self, datagram: &[u8]) -> Sent
    ! DatagramError

Sends datagram to the connected peer.

Errors#

  • ConnectionRefused if the peer reported a closed port for an earlier datagram.

  • MessageTooLarge, or another DatagramError from the system.

Method sendTo#

silk
pub effect fn sendTo(self: &mut Self, datagram: &[u8], endpoint: Endpoint) -> Sent
    ! DatagramError

Sends datagram to endpoint.

Errors#

  • FamilyUnsupported if endpoint is not of the family of the socket.

  • MessageTooLarge, NetworkUnreachable, or another DatagramError from the system.

Method receive#

silk
pub effect fn receive(self: &mut Self, buffer: &mut [u8]) -> Received
    ! DatagramError

Receives one datagram into buffer, if one is waiting.

Details#

A datagram longer than buffer is cut to its length on Linux and macOS. Use a buffer of 65535 bytes to get every datagram whole.

Errors#

  • ConnectionRefused if the socket is connected and the peer reported a closed port.

  • MessageTooLarge on Windows, for a datagram longer than buffer.

  • Another DatagramError from the system.

Method waitReadable#

silk
pub effect fn waitReadable(self: &Self, deadline: Instant) -> bool
    ! DatagramError
    ? &mut MonotonicClock

Waits until a datagram or an error is waiting on the socket, or until deadline on the MonotonicClock. Returns false if the deadline passed first.

When to use#

Use it in a program without an event loop. It blocks the thread.

Errors#

  • A DatagramError from the system.

waitAny#

silk
pub effect fn waitAny(sockets: &[NativeUdpSocket], deadline: Instant) -> bool
  ! DatagramError | OutOfMemoryError
  ? &mut MonotonicClock | &mut Allocator

Waits until a datagram or an error is waiting on any of sockets, or until deadline on the MonotonicClock. Returns false if the deadline passed first.

When to use#

Use it in a program without an event loop, then call NativeUdpSocket.receive on each socket: a socket that has nothing returns Received.WouldBlock. It blocks the thread.

Errors#

  • A DatagramError from the system.

  • OutOfMemoryError if the Allocator cannot supply the list of sockets for the system.

Category: Networking.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2, wasm64-unknown-unknown.

View the source of this page