native_socket 0.4.1 copy "native_socket: ^0.4.1" to clipboard
native_socket: ^0.4.1 copied to clipboard

Use unix sockets via file descriptors.

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.