// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; import 'package:stream_channel/isolate_channel.dart'; import 'package:stream_channel/stream_channel.dart'; Future main() async { // A StreamChannel, is in simplest terms, a wrapper around a Stream and // a StreamSink. For example, you can create a channel that wraps standard // IO: var stdioChannel = StreamChannel(stdin, stdout); stdioChannel.sink.add('Hello!\n'.codeUnits); // Like a Stream can be transformed with a StreamTransformer, a // StreamChannel can be transformed with a StreamChannelTransformer. // For example, we can handle standard input as strings: var stringChannel = stdioChannel .transform(StreamChannelTransformer.fromCodec(utf8)) .transformStream(const LineSplitter()); stringChannel.sink.add('world!\n'); // You can implement StreamChannel by extending StreamChannelMixin, but // it's much easier to use a StreamChannelController. A controller has two // StreamChannel members: `local` and `foreign`. The creator of a // controller should work with the `local` channel, while the recipient should // work with the `foreign` channel, and usually will not have direct access to // the underlying controller. var ctrl = StreamChannelController(); ctrl.local.stream.listen((event) { // Do something useful here... }); // You can also pipe events from one channel to another. ctrl ..foreign.pipe(stringChannel) ..local.sink.add('Piped!\n'); await ctrl.local.sink.close(); // The StreamChannel interface provides several guarantees, which can be // found here: // https://pub.dev/documentation/stream_channel/latest/stream_channel/StreamChannel-class.html // // By calling `StreamChannel.withGuarantees()`, you can create a // StreamChannel that provides all guarantees. var dummyCtrl0 = StreamChannelController(); var guaranteedChannel = StreamChannel.withGuarantees( dummyCtrl0.foreign.stream, dummyCtrl0.foreign.sink); // To close a StreamChannel, use `sink.close()`. await guaranteedChannel.sink.close(); // A MultiChannel multiplexes multiple virtual channels across a single // underlying transport layer. For example, an application listening over // standard I/O can still support multiple clients if it has a mechanism to // separate events from different clients. // // A MultiChannel splits events into numbered channels, which are // instances of VirtualChannel. var dummyCtrl1 = StreamChannelController(); var multiChannel = MultiChannel(dummyCtrl1.foreign); var channel1 = multiChannel.virtualChannel(); await multiChannel.sink.close(); // The client/peer should also create its own MultiChannel, connected to // the underlying transport, use the corresponding ID's to handle events in // their respective channels. It is up to you how to communicate channel ID's // across different endpoints. var dummyCtrl2 = StreamChannelController(); var multiChannel2 = MultiChannel(dummyCtrl2.foreign); var channel2 = multiChannel2.virtualChannel(channel1.id); await channel2.sink.close(); await multiChannel2.sink.close(); // Multiple instances of a Dart application can communicate easily across // `SendPort`/`ReceivePort` pairs by means of the `IsolateChannel` class. // Typically, one endpoint will create a `ReceivePort`, and call the // `IsolateChannel.connectReceive` constructor. The other endpoint will be // given the corresponding `SendPort`, and then call // `IsolateChannel.connectSend`. var recv = ReceivePort(); var recvChannel = IsolateChannel.connectReceive(recv); var sendChannel = IsolateChannel.connectSend(recv.sendPort); // You must manually close `IsolateChannel` sinks, however. await recvChannel.sink.close(); await sendChannel.sink.close(); // You can use the `Disconnector` transformer to cause a channel to act as // though the remote end of its transport had disconnected. var disconnector = Disconnector(); var disconnectable = stringChannel.transform(disconnector); disconnectable.sink.add('Still connected!'); await disconnector.disconnect(); // Additionally: // * The `DelegatingStreamController` class can be extended to build a // basis for wrapping other `StreamChannel` objects. // * The `jsonDocument` transformer converts events to/from JSON, using // the `json` codec from `dart:convert`. // * `package:json_rpc_2` directly builds on top of // `package:stream_channel`, so any compatible transport can be used to // create interactive client/server or peer-to-peer applications (i.e. // language servers, microservices, etc. }