Simple socket library for Dart

A simple library to work with Dart sockets.

The socket-io NodeJS port for Dart is pretty good but it has missing type definitions and I don’t like to work with dynamic data type. Also, it is pretty heavy, I don’t need all that stuff, all I want is to make sure I have a minimal intuitive library that I can work on top of Dart socket classes without worrying about leaking the memory because I forgot the 9th Stream listener opened.

Current implemented features:

  • Server-side supported.
  • Client-side supported.
  • Message-framing through send APIs.
  • Fully-typed API although optional.
  • Lightweight: less than 400 lines of code.
  • Event-driven callbacks.
  • Custom event types.

Basic usage:

final SimpleServerSocket server = await SimpleServerSocket.bind();

server.on<SimpleSocket>('connection', (SimpleSocket client) {
  // or: client.sendMessage('hello', 'Hi, client.');
  client.sendSignal('hello');

  client.on<void>('bye', (_) => server.destroy());
});

server.listen();

// On client-side:

final SimpleSocket socket =
    await SimpleSocket.connect('localhost', server.port);

socket.on<void>('hello', (_) => socket.replyWithSignal('bye'));

Using messages:

final SimpleServerSocket server = await SimpleServerSocket.bind();

server.on<SimpleSocket>('connection', (SimpleSocket client) {
  client.sendMessage('greetings', 'Sup client!');

  client.on<List<int>>('bye', (List<int> data) {
    print(String.fromCharCodes(data)); // Bye bye...
    server.destroy();
  });
});

server.listen();

// On client-side:

final SimpleSocket socket =
    await SimpleSocket.connect('localhost', server.port);

socket.on<List<int>>('greetings', (List<int> data) {
  print(String.fromCharCodes(data)); // Sup client!
  socket.reply('bye', 'Bye bye...');
});
That’s what looks like working with raw Dart sockets.

final ServerSocket server =
    await ServerSocket.bind(InternetAddress.anyIPv4, 0);

late final StreamSubscription<Socket> onNewClientListener;

Future<void> closeServer() async {
  await onNewClientListener.cancel();
  await server.close();
}

onNewClientListener = server.listen(
  (Socket client) {
    late final StreamSubscription<String> onNewMessageListener;

    Future<void> cancelListener() async {
      await onNewMessageListener.cancel();
    }

    client.write('Sup client!');

    onNewMessageListener = client.map(String.fromCharCodes).listen(
      // No TCP messaging-frame support!
      (String message) {
        print(message);
        if (message == 'bye') {
          client.close();
          closeServer();
        }
      },
      cancelOnError: true,
      onDone: cancelListener,
      onError: (_) => cancelListener(),
    );
  },
  cancelOnError: true,
  onDone: closeServer,
  onError: (_) => closeServer(),
);

// On client-side.

final Socket socket = await Socket.connect('localhost', server.port);

late final StreamSubscription<String> onNewServerMessageListener;

Future<void> cancelListener() async {
  socket.destroy();
  await socket.close();
  await onNewServerMessageListener.cancel();
}

onNewServerMessageListener = socket.map(String.fromCharCodes).listen(
  // No TCP messaging-frame support!
  (String message) {
    if (message.startsWith('Sup')) {
      print(message);
      socket.write('bye');
    }
  },
  cancelOnError: true,
  onDone: cancelListener,
  onError: (_) => cancelListener(),
);

Further reading

Open Source

Copyright © 2023-present, Alex Rintt.

Simple Socket is MIT licensed 💖

GitHub

View Github