Dart SocketMessage
last modified April 4, 2025
The SocketMessage
class in Dart provides a way to handle network
communication through sockets. It's useful for building client-server
applications and real-time systems.
SocketMessage manages message framing, serialization, and network transmission.
It's often used with Dart's dart:io
library for socket programming.
Basic Definition
SocketMessage
represents a discrete unit of communication over
sockets. It typically includes headers and a payload for structured data.
Key features include message framing, binary/string conversion, and error handling. It helps manage the complexity of network communication.
Basic SocketMessage Usage
This example shows basic message creation and sending through a socket.
import 'dart:io'; import 'dart:convert'; class SocketMessage { final Map<String, String> headers; final String body; SocketMessage(this.headers, this.body); List<int> toBytes() { var headerStr = headers.entries .map((e) => '${e.key}:${e.value}') .join(';'); return utf8.encode('$headerStr|$body'); } } void main() async { var server = await ServerSocket.bind('127.0.0.1', 4040); print('Server listening on ${server.address}:${server.port}'); server.listen((client) { client.transform(utf8.decoder).listen((data) { print('Received: $data'); }); }); var client = await Socket.connect('127.0.0.1', 4040); var message = SocketMessage( {'type': 'greeting', 'length': '5'}, 'Hello' ); client.add(message.toBytes()); }
We create a simple SocketMessage class with headers and body. The server listens for connections while the client sends a formatted message. The message is converted to bytes for transmission.
$ dart main.dart Server listening on 127.0.0.1:4040 Received: type:greeting;length:5|Hello
Handling Binary Data
This example demonstrates working with binary data in SocketMessage.
import 'dart:io'; import 'dart:typed_data'; class BinarySocketMessage { final int type; final Uint8List data; BinarySocketMessage(this.type, this.data); Uint8List toBytes() { var buffer = ByteData(4 + data.length); buffer.setUint32(0, type); buffer.buffer.asUint8List().setAll(4, data); return buffer.buffer.asUint8List(); } } void main() async { var server = await ServerSocket.bind('127.0.0.1', 4041); server.listen((client) { client.listen((data) { var bytes = Uint8List.fromList(data); var type = ByteData.sublistView(bytes, 0, 4).getUint32(0); print('Received message type: $type'); }); }); var client = await Socket.connect('127.0.0.1', 4041); var message = BinarySocketMessage( 42, Uint8List.fromList([1, 2, 3, 4, 5]) ); client.add(message.toBytes()); }
We create a binary message format with a 4-byte type header followed by payload. The server extracts the message type from the binary data. This is efficient for binary protocols.
$ dart main.dart Received message type: 42
Message Framing
This example shows how to implement message framing with length prefixes.
import 'dart:io'; import 'dart:typed_data'; import 'dart:convert'; class FramedSocketMessage { final String content; FramedSocketMessage(this.content); Uint8List toBytes() { var contentBytes = utf8.encode(content); var buffer = ByteData(4 + contentBytes.length); buffer.setUint32(0, contentBytes.length); buffer.buffer.asUint8List().setAll(4, contentBytes); return buffer.buffer.asUint8List(); } } void main() async { var server = await ServerSocket.bind('127.0.0.1', 4042); server.listen((client) { var buffer = <int>[]; client.listen((data) { buffer.addAll(data); if (buffer.length >= 4) { var length = ByteData.sublistView(Uint8List.fromList(buffer), 0, 4) .getUint32(0); if (buffer.length >= 4 + length) { var message = utf8.decode(buffer.sublist(4, 4 + length)); print('Framed message: $message'); buffer = buffer.sublist(4 + length); } } }); }); var client = await Socket.connect('127.0.0.1', 4042); var message = FramedSocketMessage('Hello, framed world!'); client.add(message.toBytes()); }
We implement length-prefixed message framing. The message starts with a 4-byte length followed by the payload. The server reads the length first then the exact payload bytes. This 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:io'; import 'dart:convert'; class JsonSocketMessage { final String type; final Map<String, dynamic> payload; JsonSocketMessage(this.type, this.payload); List<int> toBytes() { var message = { 'type': type, 'payload': payload, 'timestamp': DateTime.now().toIso8601String() }; return utf8.encode(json.encode(message)); } } void main() async { var server = await ServerSocket.bind('127.0.0.1', 4043); server.listen((client) { client.transform(utf8.decoder).listen((data) { var message = json.decode(data); print('${message['type']}: ${message['payload']}'); }); }); var client = await Socket.connect('127.0.0.1', 4043); var message = JsonSocketMessage( 'user.login', {'username': 'johndoe', 'password': 'secret'} ); client.add(message.toBytes()); }
We create JSON-formatted messages with type and payload fields. The server decodes the JSON and extracts 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:io'; import 'dart:convert'; class SafeSocketMessage { final String command; final List<String> args; SafeSocketMessage(this.command, this.args); List<int> toBytes() { try { return utf8.encode('$command:${args.join(',')}'); } catch (e) { return utf8.encode('error:Failed to encode message'); } } } void main() async { var server = await ServerSocket.bind('127.0.0.1', 4044); server.listen((client) { client.transform(utf8.decoder).listen( (data) { try { var parts = data.split(':'); if (parts.length != 2) throw FormatException('Invalid format'); print('Command: ${parts[0]}, Args: ${parts[1].split(',')}'); } catch (e) { print('Error processing message: $e'); client.write('error:Invalid message format'); } }, onError: (e) => print('Connection error: $e'), onDone: () => print('Client disconnected') ); }); var client = await Socket.connect('127.0.0.1', 4044); var message = SafeSocketMessage('calculate', ['add', '5', '3']); client.add(message.toBytes()); client.listen((response) { print('Server response: ${utf8.decode(response)}'); client.close(); }); }
We implement error handling at both message creation and processing stages. The client sends a structured command message while the server validates it. Error responses are sent back when problems occur.
$ dart main.dart Command: calculate, Args: [add, 5, 3] Client disconnected
Best Practices
- Message Framing: Always implement proper message boundaries
- Error Handling: Validate messages and handle errors gracefully
- Serialization: Choose appropriate formats (JSON, binary, etc.)
- Resource Management: Close sockets properly
- Timeouts: Implement timeouts for network operations
Source
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.