Skip to content

Libp2p Cookbook

This cookbook provides practical, goal-oriented recipes for common tasks you might want to accomplish with dart-libp2p.

This recipe demonstrates how to build a simple, command-line chat application where two peers can exchange messages directly.

First, we define a simple protocol for our chat application. When a peer wants to send a message, it will open a stream to the other peer using the protocol ID /chat/1.0.0 and write the message to it, followed by a newline character.

We’ll create a ChatClient class to encapsulate the logic for sending and receiving messages.

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dart_libp2p/dart_libp2p.dart';
class ChatClient {
final Host host;
static const String protocolId = '/chat/1.0.0';
ChatClient(this.host) {
// Set a stream handler for our chat protocol
host.setStreamHandler(protocolId, _handleIncomingMessage);
}
// Handler for incoming chat messages
void _handleIncomingMessage(P2PStream stream, PeerId remotePeer) {
print('[${host.id.toBase58().substring(0, 6)}] Received chat stream from [${remotePeer.toBase58().substring(0, 6)}]');
final reader = utf8.decoder.bind(stream.stream);
reader.listen(
(message) {
// Display the received message
print('\n[${remotePeer.toBase58().substring(0, 6)} says]: $message');
stdout.write('> ');
},
onDone: () => print('Chat stream with ${remotePeer.toBase58().substring(0, 6)} closed.'),
onError: (e) => print('Error on chat stream: $e'),
);
}
// Send a message to a peer
Future<void> sendMessage(PeerId targetPeer, String message) async {
try {
final stream = await host.newStream(targetPeer, [protocolId]);
await stream.write(utf8.encode(message + '\n'));
// The stream will be closed by the receiver after they read the message.
// For a more robust chat, you'd keep the stream open.
await stream.close();
} catch (e) {
print('Error sending message to ${targetPeer.toBase58()}: $e');
}
}
}

Now, let’s wire everything together. We’ll create two hosts, connect them, and then start a loop to read messages from the command line and send them.

Future<Host> createHost() async {
// ... (use the createHost function from the Getting Started guide)
}
void main() async {
// Create two hosts
final host1 = await createHost();
final host2 = await createHost();
print('Host 1: ${host1.id.toBase58()} listening on ${host1.addrs}');
print('Host 2: ${host2.id.toBase58()} listening on ${host2.addrs}');
// Create chat clients for each host
final chat1 = ChatClient(host1);
final chat2 = ChatClient(host2);
// Connect host1 to host2
await host1.connect(AddrInfo(host2.id, host2.addrs));
print('\n--- Chat session started! ---');
print('Type a message and press Enter to send.');
print('Messages from you will be sent from [${host1.id.toBase58().substring(0, 6)}].');
print('---------------------------\n');
// Start a loop to read from stdin and send messages
stdin.transform(utf8.decoder).transform(LineSplitter()).listen((line) {
chat1.sendMessage(host2.id, line);
stdout.write('> ');
});
stdout.write('> ');
}

When you run this code, you’ll see the PeerIDs and listen addresses for both hosts. The application will then wait for you to type a message. Anything you type will be sent from Host 1 to Host 2, and Host 2 will print it to the console.

This simple example illustrates the power of libp2p for building peer-to-peer applications. You can easily extend this to handle multiple peers, message history, and more complex interactions.