ZetCode

Dart RawServerSocket

last modified April 4, 2025

The RawServerSocket class in Dart provides low-level TCP server socket functionality. It's part of Dart's dart:io library for non-web applications.

RawServerSocket allows direct byte-level communication with TCP clients. It's useful for custom protocols, performance-critical applications, and binary data.

Basic Definition

RawServerSocket is a server-side socket that listens for incoming TCP connections. It provides raw byte streams without higher-level protocol handling.

Key features include asynchronous operation, connection event handling, and direct access to socket streams. It's the foundation for building custom network servers.

Basic Echo Server

This example shows a simple echo server using RawServerSocket.

main.dart
import 'dart:io';

void main() async {
  var server = await RawServerSocket.bind('127.0.0.1', 4040);
  print('Echo server listening on ${server.address}:${server.port}');

  server.listen((client) {
    client.listen((data) {
      client.add(data); // Echo back received data
    }, onDone: () => client.close());
  });
}

We create a server that listens on localhost port 4040. When clients connect, we echo back any data they send. The server handles multiple clients asynchronously.

$ dart main.dart
Echo server listening on 127.0.0.1:4040

Handling Client Connections

This example demonstrates detailed client connection handling.

main.dart
import 'dart:io';

void main() async {
  var server = await RawServerSocket.bind('0.0.0.0', 4041);
  print('Server started on port ${server.port}');

  server.listen((client) {
    print('Client connected from ${client.remoteAddress.address}');
    
    client.handleError((error) {
      print('Client error: $error');
    }, test: (error) => true);
    
    client.listen((data) {
      print('Received ${data.length} bytes');
      client.add([0x01, 0x02, 0x03]); // Send response
    }, onDone: () {
      print('Client disconnected');
      client.close();
    });
  });
}

We log client connections, handle errors, and send a fixed response. The remoteAddress property identifies connecting clients. Error handling ensures server stability.

$ dart main.dart
Server started on port 4041
Client connected from 127.0.0.1
Received 5 bytes
Client disconnected

Broadcast Server

This example shows a server that broadcasts messages to all connected clients.

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

void main() async {
  var server = await RawServerSocket.bind('127.0.0.1', 4042);
  var clients = <RawSocket>[];
  
  server.listen((client) {
    clients.add(client);
    print('New client (${clients.length} total)');
    
    client.listen((_) {}, onDone: () {
      clients.remove(client);
      print('Client disconnected (${clients.length} remaining)');
    });
  });

  // Broadcast timer
  Timer.periodic(Duration(seconds: 2), (_) {
    var message = 'Server time: ${DateTime.now()}\n'.codeUnits;
    for (var client in clients) {
      client.add(message);
    }
  });
}

We maintain a list of connected clients and broadcast time updates every 2 seconds. The server tracks client connections and disconnections.

$ dart main.dart
New client (1 total)
New client (2 total)
Client disconnected (1 remaining)

Port Scanner

This example demonstrates using RawServerSocket to check port availability.

main.dart
import 'dart:io';

Future<bool> isPortAvailable(int port) async {
  try {
    var server = await RawServerSocket.bind('127.0.0.1', port);
    await server.close();
    return true;
  } catch (e) {
    return false;
  }
}

void main() async {
  var ports = [80, 4040, 8080, 3000];
  
  for (var port in ports) {
    var available = await isPortAvailable(port);
    print('Port $port: ${available ? 'available' : 'in use'}');
  }
}

We attempt to bind to each port to check availability. Successful binding means the port is available. The server is immediately closed after checking.

$ dart main.dart
Port 80: in use
Port 4040: available
Port 8080: in use
Port 3000: available

Custom Protocol Server

This example implements a simple custom binary protocol server.

main.dart
import 'dart:io';

void main() async {
  var server = await RawServerSocket.bind('127.0.0.1', 4043);
  print('Custom protocol server started');

  server.listen((client) {
    var buffer = <int>[];
    
    client.listen((data) {
      buffer.addAll(data);
      
      // Process complete messages (delimited by 0xFF)
      while (buffer.contains(0xFF)) {
        var messageEnd = buffer.indexOf(0xFF);
        var message = buffer.sublist(0, messageEnd);
        buffer.removeRange(0, messageEnd + 1);
        
        print('Received message: $message');
        client.add([...message.reversed, 0xFF]); // Send reversed
      }
    }, onDone: () => client.close());
  });
}

We implement a simple protocol where messages end with 0xFF. The server processes complete messages and responds with reversed data. Partial messages are buffered.

$ dart main.dart
Custom protocol server started
Received message: [72, 101, 108, 108, 111]

Best Practices

Source

Dart RawServerSocket Documentation

This tutorial covered Dart's RawServerSocket class with practical examples showing server creation, client handling, and custom protocol implementation.

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.