ZetCode

Dart SocketControlMessage

last modified October 18, 2026

The SocketControlMessage class in Dart represents a control message that travels with data on a socket. Control messages carry platform-specific information and can also transfer open resource handles.

Control messages are received as part of a SocketMessage from RawSocket.readMessage. They are sent with RawSocket.sendMessage. This functionality is part of Dart's dart:io library and is used with Unix domain sockets on platforms that support handle passing.

Basic Definition

SocketControlMessage is an abstract class representing a platform-specific control message. It is used with RawSocket, not with RawDatagramSocket.

A control message is created with SocketControlMessage.fromHandles. The received object exposes the platform-specific level, type, and raw data bytes, and extractHandles returns any transferred ResourceHandle objects.

Basic SocketControlMessage Usage

This example sends a file handle in a control message over a Unix domain socket.

main.dart
import 'dart:async';
import 'dart:io';

Future<void> main() async {
  var path = '/tmp/dart_control_message.sock';
  try {
    File(path).deleteSync();
  } catch (_) {}

  var address = InternetAddress(path, type: InternetAddressType.unix);
  var server = await RawServerSocket.bind(address, 0);

  var received = Completer<void>();

  server.listen((client) {
    client.listen((event) {
      if (event == RawSocketEvent.read) {
        var message = client.readMessage();
        if (message != null) {
          print('Received ${message.controlMessages.length} control message(s)');
          for (var control in message.controlMessages) {
            print('level: ${control.level}, type: ${control.type}');
          }
          received.complete();
          client.close();
        }
      }
    });
  });

  var client = await RawSocket.connect(address, 0);
  var file = File('/tmp/dart_control_resource.txt')
    ..writeAsStringSync('handle data');
  var handle = ResourceHandle.fromFile(file.openSync());
  var control = SocketControlMessage.fromHandles([handle]);

  client.sendMessage([control], [1, 2, 3]);

  await received.future;
  await client.close();
  await server.close();
}

We create a Unix domain socket server and client. The client attaches a file handle to a control message and sends it together with the payload. The server receives a SocketMessage; its controlMessages list contains the control message. The level and type values are platform-specific.

$ dart main.dart
Received 1 control message(s)
level: 1, type: 1

Working with Control Message Data

This example inspects the raw payload and control message data.

main.dart
import 'dart:async';
import 'dart:io';

Future<void> main() async {
  var path = '/tmp/dart_control_data.sock';
  try {
    File(path).deleteSync();
  } catch (_) {}

  var address = InternetAddress(path, type: InternetAddressType.unix);
  var server = await RawServerSocket.bind(address, 0);

  var received = Completer<void>();

  server.listen((client) {
    client.listen((event) {
      if (event == RawSocketEvent.read) {
        var message = client.readMessage();
        if (message != null) {
          print('Data bytes: ${message.data}');
          for (var control in message.controlMessages) {
            print('Control data bytes: ${control.data}');
          }
          received.complete();
          client.close();
        }
      }
    });
  });

  var client = await RawSocket.connect(address, 0);
  var file = File('/tmp/dart_control_data.txt')
    ..writeAsStringSync('control data');
  var handle = ResourceHandle.fromFile(file.openSync());
  var control = SocketControlMessage.fromHandles([handle]);

  client.sendMessage([control], [10, 20, 30]);

  await received.future;
  await client.close();
  await server.close();
}

We inspect SocketMessage.data for the payload and SocketControlMessage.data for the raw control bytes. The format of the control data is defined by the operating system and protocol.

$ dart main.dart
Data bytes: [10, 20, 30]
Control data bytes: [13, 0, 0, 0]

Sending Control Messages

This example shows how to send packets with control messages attached.

main.dart
import 'dart:async';
import 'dart:io';

Future<void> main() async {
  var path = '/tmp/dart_control_send.sock';
  try {
    File(path).deleteSync();
  } catch (_) {}

  var address = InternetAddress(path, type: InternetAddressType.unix);
  var server = await RawServerSocket.bind(address, 0);

  var received = Completer<void>();

  server.listen((client) {
    client.listen((event) {
      if (event == RawSocketEvent.read) {
        var message = client.readMessage();
        if (message != null) {
          print('Received payload: ${message.data}');
          for (var control in message.controlMessages) {
            var handles = control.extractHandles();
            for (var handle in handles) {
              var file = handle.toFile();
              print('Received handle content: '
                  '${String.fromCharCodes(file.readSync(32))}');
              file.closeSync();
            }
          }
          received.complete();
          client.close();
        }
      }
    });
  });

  var client = await RawSocket.connect(address, 0);
  var file = File('/tmp/dart_control_send.txt')
    ..writeAsStringSync('hello handle');
  var raf = file.openSync();
  var control = SocketControlMessage.fromHandles([
    ResourceHandle.fromFile(raf)
  ]);

  client.sendMessage([control], 'payload'.codeUnits);

  await received.future;
  await client.close();
  await server.close();
}

The receiver calls extractHandles and converts each ResourceHandle to a RandomAccessFile with toFile. This is how open file descriptors are passed between processes over a Unix domain socket.

$ dart main.dart
Received payload: [112, 97, 121, 108, 111, 97, 100]
Received handle content: hello handle

Handling Multiple Control Messages

This example demonstrates processing different types of control messages.

main.dart
import 'dart:async';
import 'dart:io';

Future<void> main() async {
  var path = '/tmp/dart_control_multiple.sock';
  try {
    File(path).deleteSync();
  } catch (_) {}

  var address = InternetAddress(path, type: InternetAddressType.unix);
  var server = await RawServerSocket.bind(address, 0);

  var received = Completer<void>();

  server.listen((client) {
    client.listen((event) {
      if (event == RawSocketEvent.read) {
        var message = client.readMessage();
        if (message != null) {
          print('Control messages: ${message.controlMessages.length}');
          for (var control in message.controlMessages) {
            var handles = control.extractHandles();
            for (var handle in handles) {
              var file = handle.toFile();
              print('Handle content: '
                  '${String.fromCharCodes(file.readSync(32))}');
              file.closeSync();
            }
          }
          received.complete();
          client.close();
        }
      }
    });
  });

  var client = await RawSocket.connect(address, 0);
  var first = File('/tmp/dart_control_first.txt')
    ..writeAsStringSync('first');
  var second = File('/tmp/dart_control_second.txt')
    ..writeAsStringSync('second');

  var firstMessage = SocketControlMessage.fromHandles([
    ResourceHandle.fromFile(first.openSync())
  ]);
  var secondMessage = SocketControlMessage.fromHandles([
    ResourceHandle.fromFile(second.openSync())
  ]);

  client.sendMessage([firstMessage, secondMessage], [9, 8, 7]);

  await received.future;
  await client.close();
  await server.close();
}

The sendMessage method accepts a list of control messages. The receiver iterates over the received control messages and extracts the handles from each one.

$ dart main.dart
Control messages: 1
Handle content: first
Handle content: second

Platform-Specific Control Messages

This example shows platform-specific handling of control messages.

main.dart
import 'dart:async';
import 'dart:io';

Future<void> main() async {
  var path = '/tmp/dart_control_platform.sock';
  try {
    File(path).deleteSync();
  } catch (_) {}

  var address = InternetAddress(path, type: InternetAddressType.unix);
  var server = await RawServerSocket.bind(address, 0);

  var received = Completer<void>();

  server.listen((client) {
    client.listen((event) {
      if (event == RawSocketEvent.read) {
        var message = client.readMessage();
        if (message != null) {
          print('Platform: ${Platform.operatingSystem}');
          print('Payload: ${message.data}');
          print('Control messages: ${message.controlMessages.length}');
          for (var control in message.controlMessages) {
            print('level=${control.level}, type=${control.type}');
          }
          received.complete();
          client.close();
        }
      }
    });
  });

  var client = await RawSocket.connect(address, 0);

  if (Platform.isLinux || Platform.isMacOS) {
    var file = File('/tmp/dart_control_platform.txt')
      ..writeAsStringSync('platform data');
    var control = SocketControlMessage.fromHandles([
      ResourceHandle.fromFile(file.openSync())
    ]);
    client.sendMessage([control], [1, 2, 3]);
  } else {
    client.write([1, 2, 3]);
  }

  await received.future;
  await client.close();
  await server.close();
}

Control message support and the meaning of the level and type values vary by operating system. This example passes a handle on Linux and macOS and sends plain data elsewhere. Unix domain socket support is also platform-dependent.

$ dart main.dart
Platform: linux
Payload: [1, 2, 3]
Control messages: 1
level=1, type=1

Best Practices

Source

Dart SocketControlMessage Documentation

This tutorial covered Dart's SocketControlMessage class with practical examples showing reception, sending, and platform-specific handling of control messages.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.