Dart SocketMessage
last modified October 18, 2026
The SocketMessage class in Dart is a container for a message
received from a socket. It holds the message data and any control messages
that arrived with it.
SocketMessage is returned by RawSocket.readMessage.
It's part of Dart's dart:io library. Framing and serialization are
application-level concerns; the examples below build common patterns on top of
SocketMessage.data.
Basic Definition
SocketMessage has two fields: data, a
Uint8List containing the received bytes, and
controlMessages, a list of SocketControlMessage
objects.
On the receiving side, RawSocket.readMessage returns a
SocketMessage? when data is available. On the sending side, use
RawSocket.write for plain data, or
RawSocket.sendMessage when control messages must be attached.
Basic SocketMessage Usage
This example sends a text message through a socket and reads it as a
SocketMessage.
import 'dart:io';
void main() async {
var server = await RawServerSocket.bind('127.0.0.1', 4040);
print('Server listening on port ${server.port}');
server.listen((client) {
client.listen((event) {
if (event == RawSocketEvent.read) {
var message = client.readMessage();
if (message != null) {
print('Data: ${String.fromCharCodes(message.data)}');
print('Control messages: ${message.controlMessages.length}');
client.close();
}
}
});
});
var client = await RawSocket.connect('127.0.0.1', 4040);
client.write('Hello, SocketMessage!'.codeUnits);
await Future.delayed(Duration(seconds: 1));
await client.close();
await server.close();
}
We create a RawServerSocket and a RawSocket client.
The client writes a UTF-8 string. The server calls readMessage to
receive a SocketMessage and prints its data and control message
count.
$ dart main.dart Server listening on port 4040 Data: Hello, SocketMessage! Control messages: 0
Handling Binary Data
This example demonstrates working with binary data in SocketMessage.
import 'dart:io';
import 'dart:typed_data';
void main() async {
var server = await RawServerSocket.bind('127.0.0.1', 4041);
server.listen((client) {
client.listen((event) {
if (event == RawSocketEvent.read) {
var message = client.readMessage();
if (message != null && message.data.length >= 4) {
var type = ByteData.sublistView(message.data, 0, 4)
.getUint32(0);
print('Received message type: $type');
print('Payload: ${message.data.sublist(4)}');
client.close();
}
}
});
});
var client = await RawSocket.connect('127.0.0.1', 4041);
var payload = Uint8List.fromList([1, 2, 3, 4, 5]);
var buffer = ByteData(4 + payload.length);
buffer.setUint32(0, 42);
buffer.buffer.asUint8List().setAll(4, payload);
client.write(buffer.buffer.asUint8List());
await Future.delayed(Duration(seconds: 1));
await client.close();
await server.close();
}
We build a binary message with a 4-byte type header followed by a payload. The
server reads a SocketMessage and extracts the type from
message.data. This is efficient for binary protocols.
$ dart main.dart Received message type: 42 Payload: [1, 2, 3, 4, 5]
Message Framing
This example shows how to implement message framing with length prefixes.
import 'dart:io';
import 'dart:typed_data';
import 'dart:convert';
void main() async {
var server = await RawServerSocket.bind('127.0.0.1', 4042);
server.listen((client) {
var buffer = <int>[];
client.listen((event) {
if (event == RawSocketEvent.read) {
var message = client.readMessage();
if (message == null) return;
buffer.addAll(message.data);
while (buffer.length >= 4) {
var length = ByteData.sublistView(
Uint8List.fromList(buffer), 0, 4).getUint32(0);
if (buffer.length < 4 + length) break;
var content = utf8.decode(buffer.sublist(4, 4 + length));
print('Framed message: $content');
buffer = buffer.sublist(4 + length);
client.close();
}
}
});
});
var client = await RawSocket.connect('127.0.0.1', 4042);
var content = utf8.encode('Hello, framed world!');
var frame = ByteData(4 + content.length);
frame.setUint32(0, content.length);
frame.buffer.asUint8List().setAll(4, content);
client.write(frame.buffer.asUint8List());
await Future.delayed(Duration(seconds: 1));
await client.close();
await server.close();
}
We implement length-prefixed message framing on top of
SocketMessage.data. The message starts with a 4-byte length followed
by the payload. The server buffers bytes until a complete frame is available,
which handles message boundaries correctly.
$ dart main.dart Framed message: Hello, framed world!
JSON Message Format
This example demonstrates using JSON for structured message content.
import 'dart:convert';
import 'dart:io';
void main() async {
var server = await RawServerSocket.bind('127.0.0.1', 4043);
server.listen((client) {
client.listen((event) {
if (event == RawSocketEvent.read) {
var message = client.readMessage();
if (message != null) {
var decoded = json.decode(utf8.decode(message.data));
print('${decoded['type']}: ${decoded['payload']}');
client.close();
}
}
});
});
var client = await RawSocket.connect('127.0.0.1', 4043);
var message = {
'type': 'user.login',
'payload': {'username': 'johndoe', 'password': 'secret'},
'timestamp': DateTime.now().toIso8601String()
};
client.write(utf8.encode(json.encode(message)));
await Future.delayed(Duration(seconds: 1));
await client.close();
await server.close();
}
We encode structured data as JSON in the bytes that become
SocketMessage.data. The server decodes the JSON and extracts the
message components. This is useful for web APIs and structured communication.
$ dart main.dart
user.login: {username: johndoe, password: secret}
Error Handling
This example shows robust error handling in socket message processing.
import 'dart:convert';
import 'dart:io';
void main() async {
var server = await RawServerSocket.bind('127.0.0.1', 4044);
server.listen((client) {
client.listen(
(event) {
if (event == RawSocketEvent.read) {
var message = client.readMessage();
if (message == null) return;
try {
var data = utf8.decode(message.data);
var parts = data.split(':');
if (parts.length != 2) throw FormatException('Invalid format');
print('Command: ${parts[0]}, Args: ${parts[1].split(',')}');
client.write(utf8.encode('ok'));
} catch (e) {
print('Error processing message: $e');
client.write(utf8.encode('error:Invalid message format'));
}
} else if (event == RawSocketEvent.readClosed ||
event == RawSocketEvent.closed) {
client.close();
}
},
onError: (e) => print('Connection error: $e'),
onDone: () {
print('Client disconnected');
client.close();
},
);
});
var client = await RawSocket.connect('127.0.0.1', 4044);
client.write(utf8.encode('calculate:add,5,3'));
client.listen((event) {
if (event == RawSocketEvent.read) {
var response = client.read();
if (response != null) {
print('Server response: ${utf8.decode(response)}');
}
}
});
await Future.delayed(Duration(seconds: 1));
await client.close();
await server.close();
}
We read the message as a SocketMessage, validate its data, and
send an error response if parsing fails. The client prints the server response
and then closes the connection.
$ dart main.dart Command: calculate, Args: [add, 5, 3] Server response: ok Client disconnected
Best Practices
- Message Framing: Add your own boundaries; a
SocketMessageis one read, not a complete protocol frame - Error Handling:
readMessagemay return null and data may be malformed - Serialization: Choose appropriate formats (JSON, binary, etc.)
- Control messages: Inspect
controlMessageswhen handles or platform data are expected - Resource Management: Close sockets properly
Source
Dart SocketMessage Documentation
This tutorial covered Dart's SocketMessage patterns with practical examples showing text/binary formats, framing, JSON, and error handling for robust network communication.
Author
List all Dart tutorials.