Native Socket

Low-level Unix domain socket operations with a high-level Dart API.

Provides stream and datagram Unix domain sockets, filesystem-path and abstract namespace addressing, SCM_RIGHTS file descriptor passing, server sockets, native (off-heap) buffers, concurrent read/write via split halves, and prebuilt release assets for Linux and Android.

macOS is supported via source fallback.

Features

  • Stream sockets (SOCK_STREAM) — connection-oriented, ordered, reliable
  • Datagram sockets (SOCK_DGRAM) — connectionless, message boundaries preserved
  • Address typesAddress.file() for filesystem paths, Address.abstract() for Linux abstract namespace
  • Client-serverbind(), listen(), accept(), connect()
  • File descriptor passing — send and receive fds via SCM_RIGHTS
  • Socket pairUnixSocket.pair() for intra-process communication
  • Native buffers — heap-backed and off-heap buffers with zero-copy slicing
  • Concurrent I/Osplit() into SocketReader / SocketWriter halves
  • Socket optionsSO_SNDBUF, SO_RCVBUF, SO_LINGER
  • Non-blocking data checkhasData getter, waitForData(timeout)
  • Path cleanupunlink() / closeAndUnlink()

Installation

dependencies:
  native_socket: ^0.4.0

Note: This package uses Dart native assets (hooks/build system). Enable the experiment when running:

dart --enable-experiment=native-assets run

Tag releases publish Linux and Android (arm64, arm, x64) prebuilt assets from GitHub Releases. macOS builds use source fallback.

Prerequisites

  • Dart SDK >=3.8.0 <4.0.0
  • Linux, Android, and macOS
  • Clang (for native code compilation via hooks)
  • Android NDK (for Android builds)

Usage

Socket pair (intra-process)

import 'package:native_socket/native_socket.dart';

void main() {
  final (a, b) = UnixSocket.pair();
  a.send(Uint8List.fromList([0xDE, 0xAD]));
  final data = b.receive(1024);
  print(data); // [222, 173]
  a.close();
  b.close();
}

Client-server with bind/connect/accept

import 'package:native_socket/native_socket.dart';

void main() {
  final server = UnixSocket.bind(Address.file('/tmp/my.sock'));
  final client = UnixSocket.connect(Address.file('/tmp/my.sock'));

  client.send(Uint8List.fromList([0x41, 0x42]));
  final conn = server.accept();
  final data = conn.receive(1024);
  print(data); // [65, 66]

  conn.close();
  client.close();
  server.closeAndUnlink();
}

Datagram sockets (message boundaries preserved)

import 'package:native_socket/native_socket.dart';

void main() {
  final server = UnixSocket.bind(
    Address.file('/tmp/dgram.sock'),
    type: SocketType.datagram,
  );
  final client = UnixSocket.create(type: SocketType.datagram);

  client.sendTo(Address.file('/tmp/dgram.sock'), Uint8List.fromList([0x01]));
  client.sendTo(Address.file('/tmp/dgram.sock'), Uint8List.fromList([0x02, 0x03]));

  final first = server.receiveFrom(1024);  // [0x01]
  final second = server.receiveFrom(1024); // [0x02, 0x03]

  client.close();
  server.closeAndUnlink();
}

Abstract namespace (Linux only)

import 'package:native_socket/native_socket.dart';

void main() {
  final addr = Address.abstract('my-service');
  final server = UnixSocket.bind(addr);
  final client = UnixSocket.connect(addr);
  // ... no filesystem path to clean up
  server.close();
  client.close();
}

Native buffers for zero-copy I/O

import 'package:native_socket/native_socket.dart';

void main() {
  // Heap-backed (convenient)
  final heapBuf = NativeBuffer.fromList(Uint8List.fromList([1, 2, 3]));

  // Native/off-heap (zero-copy for FFI, must free)
  final nativeBuf = NativeBuffer.allocate(1024);
  nativeBuf.asTypedList()[0] = 0xFF;
  // ... use nativeBuf.nativePointer in FFI calls ...
  nativeBuf.free();

  // Zero-copy slice (shares underlying memory)
  final sliced = heapBuf.slice(1, 2); // [2, 3]
}

SCM_RIGHTS file descriptor passing

import 'package:native_socket/native_socket.dart';

void main() {
  final (a, b) = UnixSocket.pair();
  final fileFd = createAnonymousFile(4096);

  // Send data + fd together
  a.send(Uint8List.fromList([0xFF]), fd: fileFd);

  // Receive fd (recvmsg reads both data byte and fd)
  final receivedFd = b.receiveFd();
  closeFd(receivedFd);
  closeFd(fileFd);
  a.close();
  b.close();
}

Socket options

import 'package:native_socket/native_socket.dart';

void main() {
  final (a, _) = UnixSocket.pair();
  a.setSendBufferSize(65536);
  print(a.getSendBufferSize()); // kernel may round up
  a.setLinger(true, 5);
  a.close();
}

Split for concurrent read/write

import 'package:native_socket/native_socket.dart';

void main() {
  final (a, b) = UnixSocket.pair();
  final (reader, writer) = a.split();
  // reader and writer can be used from separate isolates/threads
  writer.send(Uint8List.fromList([0x01]));
  final data = b.receive(1024);
  print(data); // [1]
  a.close();
  b.close();
}

API Reference

See the full API docs on pub.dev.

Releases

Tag builds use native_prebuilt to generate the release manifest and publish Linux and Android (arm64, arm, x64) release assets on GitHub.

Contributing

Contributions welcome! Please open issues or submit pull requests on GitHub.

Libraries

native_socket
Native bindings for Unix domain sockets.