blob: 96d41178809f3131dc2f4dce673ffdcb7a719484 [file] [log] [blame] [edit]
// Copyright (c) 2013, 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.
part of "dart:io";
/// The type, or address family, of an [InternetAddress].
///
/// Currently, IP version 4 (IPv4), IP version 6 (IPv6)
/// and Unix domain address are supported.
/// Unix domain sockets are available only on Linux, MacOS and Android.
final class InternetAddressType {
static const InternetAddressType IPv4 = InternetAddressType._(0);
static const InternetAddressType IPv6 = InternetAddressType._(1);
static const InternetAddressType unix = InternetAddressType._(2);
static const InternetAddressType any = InternetAddressType._(-1);
final int _value;
const InternetAddressType._(this._value);
factory InternetAddressType._from(int value) {
if (value == IPv4._value) return IPv4;
if (value == IPv6._value) return IPv6;
if (value == unix._value) return unix;
throw ArgumentError("Invalid type: $value");
}
/// Get the name of the type, e.g. "IPv4" or "IPv6".
String get name => const ["ANY", "IPv4", "IPv6", "Unix"][_value + 1];
String toString() => "InternetAddressType: $name";
}
/// An internet address or a Unix domain address.
///
/// This object holds an internet address. If this internet address
/// is the result of a DNS lookup, the address also holds the hostname
/// used to make the lookup.
/// An Internet address combined with a port number represents an
/// endpoint to which a socket can connect or a listening socket can
/// bind.
abstract interface class InternetAddress {
/// IP version 4 loopback address.
///
/// Use this address when listening on or connecting
/// to the loopback adapter using IP version 4 (IPv4).
external static InternetAddress get loopbackIPv4;
/// IP version 6 loopback address.
///
/// Use this address when listening on or connecting to
/// the loopback adapter using IP version 6 (IPv6).
external static InternetAddress get loopbackIPv6;
/// IP version 4 any address.
///
/// Use this address when listening on the addresses
/// of all adapters using IP version 4 (IPv4).
external static InternetAddress get anyIPv4;
/// IP version 6 any address.
///
/// Use this address when listening on the addresses
/// of all adapters using IP version 6 (IPv6).
external static InternetAddress get anyIPv6;
/// The address family of the [InternetAddress].
InternetAddressType get type;
/// The numeric address of the host.
///
/// For IPv4 addresses this is using the dotted-decimal notation.
/// For IPv6 it is using the hexadecimal representation.
/// For Unix domain addresses, this is a file path.
String get address;
/// The host used to lookup the address.
///
/// If there is no host associated with the address this returns the [address].
String get host;
/// The raw address of this [InternetAddress].
///
/// For an IP address, the result is either a 4 or 16 byte long list.
/// For a Unix domain address, UTF-8 encoded byte sequences that represents
/// [address] is returned.
///
/// The returned list is a fresh copy, making it possible to change the list without
/// modifying the [InternetAddress].
Uint8List get rawAddress;
/// Whether the [InternetAddress] is a loopback address.
bool get isLoopback;
/// Whether the scope of the [InternetAddress] is a link-local.
bool get isLinkLocal;
/// Whether the scope of the [InternetAddress] is multicast.
bool get isMulticast;
/// Creates a new [InternetAddress] from a numeric address or a file path.
///
/// If [type] is [InternetAddressType.IPv4], [address] must be a numeric IPv4
/// address (dotted-decimal notation).
/// If [type] is [InternetAddressType.IPv6], [address] must be a numeric IPv6
/// address (hexadecimal notation).
/// If [type] is [InternetAddressType.unix], [address] must be a valid file
/// path.
/// If [type] is omitted, [address] must be either a numeric IPv4 or IPv6
/// address and the type is inferred from the format.
external factory InternetAddress(String address, {InternetAddressType? type});
/// Creates a new [InternetAddress] from the provided raw address bytes.
///
/// If the [type] is [InternetAddressType.IPv4], the [rawAddress] must have
/// length 4.
/// If the [type] is [InternetAddressType.IPv6], the [rawAddress] must have
/// length 16.
/// If the [type] is [InternetAddressType.unix], the [rawAddress] must be a
/// valid UTF-8 encoded file path.
///
/// If [type] is omitted, the [rawAddress] must have a length of either 4 or
/// 16, in which case the type defaults to [InternetAddressType.IPv4] or
/// [InternetAddressType.IPv6] respectively.
external factory InternetAddress.fromRawAddress(
Uint8List rawAddress, {
InternetAddressType? type,
});
/// Performs a reverse DNS lookup on this [address]
///
/// Returns a new [InternetAddress] with the same address, but where the [host]
/// field set to the result of the lookup.
///
/// If this address is Unix domain addresses, no lookup is performed and this
/// address is returned directly.
Future<InternetAddress> reverse();
/// Looks up the addresses of a host.
///
/// If [type] is [InternetAddressType.any], it will lookup both
/// IP version 4 (IPv4) and IP version 6 (IPv6) addresses.
/// If [type] is either [InternetAddressType.IPv4] or
/// [InternetAddressType.IPv6] it will only lookup addresses of the
/// specified type. The order of the list can, and most likely will,
/// change over time.
external static Future<List<InternetAddress>> lookup(
String host, {
InternetAddressType type = InternetAddressType.any,
});
/// Clones the given [address] with the new [host].
///
/// The [address] must be an [InternetAddress] that was created with one
/// of the static methods of this class.
external static InternetAddress _cloneWithNewHost(
InternetAddress address,
String host,
);
/// Attempts to parse [address] as a numeric address.
///
/// Returns `null` If [address] is not a numeric IPv4 (dotted-decimal
/// notation) or IPv6 (hexadecimal representation) address.
external static InternetAddress? tryParse(String address);
}
/// A [NetworkInterface] represents an active network interface on the current
/// system. It contains a list of [InternetAddress]es that are bound to the
/// interface.
abstract interface class NetworkInterface {
/// The name of the [NetworkInterface].
String get name;
/// The index of the [NetworkInterface].
int get index;
/// The list of [InternetAddress]es currently bound to this
/// [NetworkInterface].
List<InternetAddress> get addresses;
/// Whether the [list] method is supported.
///
/// The [list] method is supported on all platforms supported by Dart so this
/// property is always true.
@Deprecated("listSupported is always true.")
external static bool get listSupported;
/// Query the system for [NetworkInterface]s.
///
/// If [includeLoopback] is `true`, the returned list will include the
/// loopback device. Default is `false`.
///
/// If [includeLinkLocal] is `true`, the list of addresses of the returned
/// [NetworkInterface]s, may include link local addresses. Default is `false`.
///
/// If [type] is either [InternetAddressType.IPv4] or
/// [InternetAddressType.IPv6] it will only lookup addresses of the
/// specified type. Default is [InternetAddressType.any].
external static Future<List<NetworkInterface>> list({
bool includeLoopback = false,
bool includeLinkLocal = false,
InternetAddressType type = InternetAddressType.any,
});
}
/// A listening socket.
///
/// A `RawServerSocket` and provides a stream of low-level [RawSocket] objects,
/// one for each connection made to the listening socket.
///
/// See [RawSocket] for more information.
abstract interface class RawServerSocket implements Stream<RawSocket> {
/// Listens on a given address and port.
///
/// When the returned future completes the server socket is bound
/// to the given [address] and [port] and has started listening on it.
///
/// The [address] can either be a [String] or an
/// [InternetAddress]. If [address] is a [String], [bind] will
/// perform a [InternetAddress.lookup] and use the first value in the
/// list. To listen on the loopback adapter, which will allow only
/// incoming connections from the local host, use the value
/// [InternetAddress.loopbackIPv4] or
/// [InternetAddress.loopbackIPv6]. To allow for incoming
/// connection from the network use either one of the values
/// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
/// bind to all interfaces or the IP address of a specific interface.
///
/// If an IP version 6 (IPv6) address is used, both IP version 6
/// (IPv6) and version 4 (IPv4) connections will be accepted. To
/// restrict this to version 6 (IPv6) only, use [v6Only] to set
/// version 6 only.
///
/// If [port] has the value `0` an ephemeral port will
/// be chosen by the system. The actual port used can be retrieved
/// using the `port` getter.
///
/// The optional argument [backlog] can be used to specify the listen
/// backlog for the underlying OS listen setup. If [backlog] has the
/// value of `0` (the default) a reasonable value will be chosen by
/// the system.
///
/// The optional argument [shared] specifies whether additional RawServerSocket
/// objects can bind to the same combination of [address], [port] and [v6Only].
/// If [shared] is `true` and more [RawServerSocket]s from this isolate or
/// other isolates are bound to the port, then the incoming connections will be
/// distributed among all the bound [RawServerSocket]s. Connections can be
/// distributed over multiple isolates this way.
external static Future<RawServerSocket> bind(
address,
int port, {
int backlog = 0,
bool v6Only = false,
bool shared = false,
});
/// The port used by this socket.
int get port;
/// The address used by this socket.
InternetAddress get address;
/// Closes the socket.
///
/// The returned future completes when the socket
/// is fully closed and is no longer bound.
Future<RawServerSocket> close();
}
/// A listening socket.
///
/// A [ServerSocket] provides a stream of [Socket] objects,
/// one for each connection made to the listening socket.
///
/// See [Socket] for more info.
abstract interface class ServerSocket implements ServerSocketBase<Socket> {
/// Listens on a given address and port.
///
/// When the returned future completes the server socket is bound
/// to the given [address] and [port] and has started listening on it.
///
/// The [address] can either be a [String] or an
/// [InternetAddress]. If [address] is a [String], [bind] will
/// perform a [InternetAddress.lookup] and use the first value in the
/// list. To listen on the loopback adapter, which will allow only
/// incoming connections from the local host, use the value
/// [InternetAddress.loopbackIPv4] or
/// [InternetAddress.loopbackIPv6]. To allow for incoming
/// connection from the network use either one of the values
/// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
/// bind to all interfaces or the IP address of a specific interface.
///
/// If an IP version 6 (IPv6) address is used, both IP version 6
/// (IPv6) and version 4 (IPv4) connections will be accepted. To
/// restrict this to version 6 (IPv6) only, use [v6Only] to set
/// version 6 only.
///
/// If [port] has the value `0` an ephemeral port will be chosen by
/// the system. The actual port used can be retrieved using the
/// [port] getter.
///
/// The optional argument [backlog] can be used to specify the listen
/// backlog for the underlying OS listen setup. If [backlog] has the
/// value of `0` (the default) a reasonable value will be chosen by
/// the system.
///
/// The optional argument [shared] specifies whether additional ServerSocket
/// objects can bind to the same combination of [address], [port] and
/// [v6Only]. If [shared] is `true` and more server sockets from this
/// isolate or other isolates are bound to the port, then the incoming
/// connections will be distributed among all the bound server sockets.
/// Connections can be distributed over multiple isolates this way.
static Future<ServerSocket> bind(
address,
int port, {
int backlog = 0,
bool v6Only = false,
bool shared = false,
}) {
final IOOverrides? overrides = IOOverrides.current;
if (overrides == null) {
return ServerSocket._bind(
address,
port,
backlog: backlog,
v6Only: v6Only,
shared: shared,
);
}
return overrides.serverSocketBind(
address,
port,
backlog: backlog,
v6Only: v6Only,
shared: shared,
);
}
external static Future<ServerSocket> _bind(
address,
int port, {
int backlog = 0,
bool v6Only = false,
bool shared = false,
});
/// The port used by this socket.
int get port;
/// The address used by this socket.
InternetAddress get address;
/// Closes the socket.
///
/// The returned future completes when the socket
/// is fully closed and is no longer bound.
Future<ServerSocket> close();
}
/// The [SocketDirection] is used as a parameter to [Socket.close] and
/// [RawSocket.close] to close a socket in the specified direction(s).
final class SocketDirection {
static const SocketDirection receive = SocketDirection._(0);
static const SocketDirection send = SocketDirection._(1);
static const SocketDirection both = SocketDirection._(2);
final _value;
const SocketDirection._(this._value);
}
/// An option for a socket which is configured using [Socket.setOption].
///
/// The [SocketOption] is used as a parameter to [Socket.setOption] and
/// [RawSocket.setOption] to customize the behaviour of the underlying
/// socket.
final class SocketOption {
/// Enable or disable no-delay on the socket. If tcpNoDelay is enabled, the
/// socket will not buffer data internally, but instead write each data chunk
/// as an individual TCP packet.
///
/// tcpNoDelay is disabled by default.
static const SocketOption tcpNoDelay = SocketOption._(0);
static const SocketOption _ipMulticastLoop = SocketOption._(1);
static const SocketOption _ipMulticastHops = SocketOption._(2);
static const SocketOption _ipMulticastIf = SocketOption._(3);
static const SocketOption _ipBroadcast = SocketOption._(4);
final _value;
const SocketOption._(this._value);
}
// Must be kept in sync with enum in socket.cc
enum _RawSocketOptions {
SOL_SOCKET, // 0
IPPROTO_IP, // 1
IP_MULTICAST_IF, // 2
IPPROTO_IPV6, // 3
IPV6_MULTICAST_IF, // 4
IPPROTO_TCP, // 5
IPPROTO_UDP, // 6
}
/// The [RawSocketOption] is used as a parameter to [Socket.setRawOption] and
/// [RawSocket.setRawOption] to customize the behaviour of the underlying
/// socket.
///
/// It allows for fine grained control of the socket options, and its values
/// will be passed to the underlying platform's implementation of setsockopt and
/// getsockopt.
final class RawSocketOption {
/// Creates a [RawSocketOption] for [RawSocket.getRawOption]
/// and [RawSocket.setRawOption].
///
/// The [level] and [option] arguments correspond to `level` and `optname` arguments
/// on the `getsockopt()` and `setsockopt()` native calls.
///
/// The value argument and its length correspond to the optval and length
/// arguments on the native call.
///
/// For a [RawSocket.getRawOption] call, the value parameter will be updated
/// after a successful call (although its length will not be changed).
///
/// For a [RawSocket.setRawOption] call, the value parameter will be used set
/// the option.
const RawSocketOption(this.level, this.option, this.value);
/// Convenience constructor for creating an integer based [RawSocketOption].
factory RawSocketOption.fromInt(int level, int option, int value) {
final Uint8List list = Uint8List(4);
final buffer = ByteData.view(list.buffer, list.offsetInBytes);
buffer.setInt32(0, value, Endian.host);
return RawSocketOption(level, option, list);
}
/// Convenience constructor for creating a boolean based [RawSocketOption].
factory RawSocketOption.fromBool(int level, int option, bool value) =>
RawSocketOption.fromInt(level, option, value ? 1 : 0);
/// The level for the option to set or get.
///
/// See also:
/// * [RawSocketOption.levelSocket]
/// * [RawSocketOption.levelIPv4]
/// * [RawSocketOption.levelIPv6]
/// * [RawSocketOption.levelTcp]
/// * [RawSocketOption.levelUdp]
final int level;
/// The numeric ID of the option to set or get.
final int option;
/// The raw data to set, or the array to write the current option value into.
///
/// This list must be the correct length for the expected option. For most
/// options that take [int] or [bool] values, the length should be 4. For options
/// that expect a struct (such as an in_addr_t), the length should be the
/// correct length for that struct.
final Uint8List value;
/// Socket level option for `SOL_SOCKET`.
static int get levelSocket =>
_getOptionValue(_RawSocketOptions.SOL_SOCKET.index);
/// Socket level option for `IPPROTO_IP`.
static int get levelIPv4 =>
_getOptionValue(_RawSocketOptions.IPPROTO_IP.index);
/// Socket option for `IP_MULTICAST_IF`.
static int get IPv4MulticastInterface =>
_getOptionValue(_RawSocketOptions.IP_MULTICAST_IF.index);
/// Socket level option for `IPPROTO_IPV6`.
static int get levelIPv6 =>
_getOptionValue(_RawSocketOptions.IPPROTO_IPV6.index);
/// Socket option for `IPV6_MULTICAST_IF`.
static int get IPv6MulticastInterface =>
_getOptionValue(_RawSocketOptions.IPV6_MULTICAST_IF.index);
/// Socket level option for `IPPROTO_TCP`.
static int get levelTcp =>
_getOptionValue(_RawSocketOptions.IPPROTO_TCP.index);
/// Socket level option for `IPPROTO_UDP`.
static int get levelUdp =>
_getOptionValue(_RawSocketOptions.IPPROTO_UDP.index);
external static int _getOptionValue(int key);
}
/// Events for the [RawDatagramSocket], [RawSecureSocket], and [RawSocket].
///
/// These event objects are used by the [Stream] behavior of the sockets
/// (for example [RawSocket.listen], [RawSocket.forEach])
/// when the socket's state change.
///
/// ```dart
/// import 'dart:convert';
/// import 'dart:io';
///
/// void main() async {
/// final socket = await RawSocket.connect("example.com", 80);
///
/// socket.listen((event) {
/// switch (event) {
/// case RawSocketEvent.read:
/// final data = socket.read();
/// if (data != null) {
/// print(ascii.decode(data));
/// }
/// break;
/// case RawSocketEvent.write:
/// socket.write(ascii.encode('GET /\r\nHost: example.com\r\n\r\n'));
/// socket.writeEventsEnabled = false;
/// break;
/// case RawSocketEvent.readClosed:
/// socket.close();
/// break;
/// case RawSocketEvent.closed:
/// break;
/// default:
/// throw "Unexpected event $event";
/// }
/// });
/// }
/// ```
class RawSocketEvent {
/// An event indicates the socket is ready to be read.
static const RawSocketEvent read = RawSocketEvent._(0);
/// An event indicates the socket is ready to write.
static const RawSocketEvent write = RawSocketEvent._(1);
/// An event indicates the reading from the socket is closed
static const RawSocketEvent readClosed = RawSocketEvent._(2);
/// An event indicates the socket is closed.
static const RawSocketEvent closed = RawSocketEvent._(3);
final int _value;
const RawSocketEvent._(this._value);
String toString() {
return const [
'RawSocketEvent.read',
'RawSocketEvent.write',
'RawSocketEvent.readClosed',
'RawSocketEvent.closed',
][_value];
}
}
/// A cancelable connection attempt.
///
/// Returned by the `startConnect` methods on client-side socket types `S`,
/// `ConnectionTask<S>` allows canceling an attempt to connect to a host.
final class ConnectionTask<S> {
/// A `Future` that completes with value that `S.connect()` would return
/// unless [cancel] is called on this [ConnectionTask].
///
/// If [cancel] is called, the future completes with a [SocketException]
/// error whose message indicates that the connection attempt was cancelled.
final Future<S> socket;
final void Function() _onCancel;
ConnectionTask._(Future<S> this.socket, void Function() onCancel)
: _onCancel = onCancel;
/// Create a `ConnectionTask` from an existing `Future<Socket>`.
///
/// You can use this method to return existing socket connections in
/// [HttpClient.connectionFactory].
///
/// For example:
///
/// ```dart
/// final clientSocketFuture = Socket.connect(
/// serverUri.host, serverUri.port);
/// final client = HttpClient()
/// ..connectionFactory = (uri, proxyHost, proxyPort) {
/// return Future.value(
/// ConnectionTask.fromSocket(clientSocketFuture, () {}));
/// final response = await client.getUrl(serverUri);
/// ```
static ConnectionTask<T> fromSocket<T extends Socket>(
Future<T> socket,
void Function() onCancel,
) => ConnectionTask<T>._(socket, onCancel);
/// Cancels the connection attempt.
///
/// This also causes the [socket] `Future` to complete with a
/// [SocketException] error.
void cancel() {
_onCancel();
}
}
/// A TCP connection.
///
/// A *socket connection* connects a *local* socket to a *remote* socket.
/// Data, as [Uint8List]s, is received by the local socket and made
/// available by the [read] method, and can be sent to the remote socket
/// through the [write] method.
///
/// The [Stream] interface of this class provides event notification about when
/// a certain change has happened, for example when data has become available
/// ([RawSocketEvent.read]) or when the remote end has stopped listening
/// ([RawSocketEvent.closed]).
abstract interface class RawSocket implements Stream<RawSocketEvent> {
/// Set or get, if the [RawSocket] should listen for [RawSocketEvent.read]
/// and [RawSocketEvent.readClosed] events. Default is `true`.
///
/// Warning: setting [readEventsEnabled] to `false` might prevent socket
/// from fully closing when [SocketDirection.receive] and
/// [SocketDirection.send] directions are shutdown independently. See
/// [shutdown] for more details.
abstract bool readEventsEnabled;
/// Set or get, if the [RawSocket] should listen for [RawSocketEvent.write]
/// events. Default is `true`.
/// This is a one-shot listener, and writeEventsEnabled must be set
/// to true again to receive another write event.
abstract bool writeEventsEnabled;
/// Creates a new socket connection to the host and port.
///
/// Returns a [Future] that will complete with either a [RawSocket]
/// once connected, or an error if the host-lookup or connection failed.
///
/// The [host] can either be a [String] or an [InternetAddress]. If [host] is a
/// [String], [connect] will perform a [InternetAddress.lookup] and try
/// all returned [InternetAddress]es, until connected. If IPv4 and IPv6
/// addresses are both available then connections over IPv4 are preferred. If
/// no connection can be established then the error from the first failing
/// connection is returned.
///
/// The argument [sourceAddress] can be used to specify the local
/// address to bind when making the connection. The [sourceAddress] can either
/// be a [String] or an [InternetAddress]. If a [String] is passed it must
/// hold a numeric IP address.
///
/// The [sourcePort] defines the local port to bind to. If [sourcePort] is
/// not specified or zero, a port will be chosen.
///
/// The argument [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established. If [timeout] is longer than the system
/// level timeout duration, a timeout may occur sooner than specified in
/// [timeout]. On timeout, a [SocketException] is thrown and all ongoing
/// connection attempts to [host] are cancelled.
external static Future<RawSocket> connect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
Duration? timeout,
});
/// Like [connect], but returns a [Future] that completes with a
/// [ConnectionTask] that can be cancelled if the [RawSocket] is no
/// longer needed.
external static Future<ConnectionTask<RawSocket>> startConnect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
});
/// The number of received and non-read bytes in the socket that can be read.
int available();
/// Read up to [len] bytes from the socket.
///
/// This function is non-blocking and will only return data
/// if data is available.
/// The number of bytes read can be less than [len] if fewer bytes are
/// available for immediate reading. If no data is available `null`
/// is returned.
Uint8List? read([int? len]);
/// Reads a message containing up to [count] bytes from the socket.
///
/// This function differs from [read] in that it will also return any
/// [SocketControlMessage] that have been sent.
///
/// This function is non-blocking and will only return data
/// if data is available.
/// The number of bytes read can be less than [count] if fewer bytes are
/// available for immediate reading.
/// Length of data buffer in [SocketMessage] indicates number of bytes read.
///
/// Returns `null` if no data is available.
///
/// Unsupported by [RawSecureSocket].
///
/// Unsupported on Android, Fuchsia, Windows.
SocketMessage? readMessage([int? count]);
/// Writes up to [count] bytes of the buffer from [offset] buffer offset to
/// the socket.
///
/// The number of successfully written bytes is returned. This function is
/// non-blocking and will only write data if buffer space is available in
/// the socket. This means that the number of successfully written bytes may
/// be less than `count` or even 0.
///
/// Transmission of the buffer may be delayed unless
/// [SocketOption.tcpNoDelay] is set with [RawSocket.setOption].
///
/// The default value for [offset] is 0, and the default value for [count] is
/// `buffer.length - offset`.
int write(List<int> buffer, [int offset = 0, int? count]);
/// Writes socket control messages and data bytes to the socket.
///
/// Writes [controlMessages] and up to [count] bytes of [data],
/// starting at [offset], to the socket. If [count] is not provided,
/// as many bytes as possible are written. Use [write] instead if no control
/// messages are required to be sent.
///
/// When sent control messages are received, they are retained until the
/// next call to [readMessage], where all currently available control messages
/// are provided as part of the returned [SocketMessage].
/// Calling [read] will read only data bytes, and will not affect control
/// messages.
///
/// The [count] must be positive (greater than zero).
///
/// Returns the number of bytes written, which cannot be greater than
/// [count], nor greater than `data.length - offset`.
/// Return value of zero indicates that control messages were not sent.
///
/// This function is non-blocking and will only write data
/// if buffer space is available in the socket.
///
/// Throws an [OSError] if message could not be sent out.
///
/// Unsupported by [RawSecureSocket].
///
/// Unsupported on Android, Fuchsia, Windows.
int sendMessage(
List<SocketControlMessage> controlMessages,
List<int> data, [
int offset = 0,
int? count,
]);
/// The port used by this socket.
///
/// Throws a [SocketException] if the socket is closed.
int get port;
/// The remote port connected to by this socket.
///
/// Throws a [SocketException] if the socket is closed.
int get remotePort;
/// The [InternetAddress] used to connect this socket.
///
/// Throws a [SocketException] if the socket is closed.
InternetAddress get address;
/// The remote [InternetAddress] connected to by this socket.
///
/// Throws a [SocketException] if the socket is closed.
InternetAddress get remoteAddress;
/// Closes the socket.
///
/// Returns a future that completes with this socket when the
/// underlying connection is completely destroyed.
///
/// Calling [close] will never throw an exception
/// and calling it several times is supported. Calling [close] can result in
/// a [RawSocketEvent.readClosed] event.
Future<RawSocket> close();
/// Shuts down the socket in the [direction].
///
/// Calling [shutdown] will never throw an exception
/// and calling it several times is supported. Calling
/// shutdown with either [SocketDirection.both] or [SocketDirection.receive]
/// can result in a [RawSocketEvent.readClosed] event.
///
/// Warning: [SocketDirection.receive] direction is only considered to be
/// to be fully shutdown once all available data is drained and
/// [RawSocketEvent.readClosed] is dispatched. Shutting down
/// [SocketDirection.receive] and [SocketDirection.send] directions separately
/// without draining the data will lead to socket staying around until the
/// data is drained. This can happen if [readEventsEnabled] is set
/// to `false` or if received data is not [read] in response to these
/// events. This does not apply to shutting down both directions
/// simultaneously using [SocketDirection.both] which will discard all
/// received data instead.
void shutdown(SocketDirection direction);
/// Customize the [RawSocket].
///
/// See [SocketOption] for available options.
///
/// Returns `true` if the option was set successfully, `false` otherwise.
bool setOption(SocketOption option, bool enabled);
/// Reads low level information about the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Returns the [RawSocketOption.value] on success.
///
/// Throws an [OSError] on failure.
Uint8List getRawOption(RawSocketOption option);
/// Customizes the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Throws an [OSError] on failure.
void setRawOption(RawSocketOption option);
}
/// A TCP connection between two sockets.
///
/// A *socket connection* connects a *local* socket to a *remote* socket.
/// Data, as [Uint8List]s, is received by the local socket, made available
/// by the [Stream] interface of this class, and can be sent to the remote
/// socket through the [IOSink] interface of this class.
///
/// Transmission of the data sent through the [IOSink] interface may be
/// delayed unless [SocketOption.tcpNoDelay] is set with
/// [Socket.setOption].
abstract interface class Socket implements Stream<Uint8List>, IOSink {
/// Creates a new socket connection to the host and port and returns a [Future]
/// that will complete with either a [Socket] once connected or an error
/// if the host-lookup or connection failed.
///
/// [host] can either be a [String] or an [InternetAddress]. If [host] is a
/// [String], [connect] will perform a [InternetAddress.lookup] and try
/// all returned [InternetAddress]es, until connected. Unless a
/// connection was established, the error from the first failing connection is
/// returned.
///
/// The argument [sourceAddress] can be used to specify the local
/// address to bind when making the connection. The [sourceAddress] can either
/// be a [String] or an [InternetAddress]. If a [String] is passed it must
/// hold a numeric IP address.
///
/// The [sourcePort] defines the local port to bind to. If [sourcePort] is
/// not specified or zero, a port will be chosen.
///
/// The argument [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established. If [timeout] is longer than the system
/// level timeout duration, a timeout may occur sooner than specified in
/// [timeout]. On timeout, a [SocketException] is thrown and all ongoing
/// connection attempts to [host] are cancelled.
static Future<Socket> connect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
Duration? timeout,
}) {
final IOOverrides? overrides = IOOverrides.current;
if (overrides == null) {
return Socket._connect(
host,
port,
sourceAddress: sourceAddress,
sourcePort: sourcePort,
timeout: timeout,
);
}
return overrides.socketConnect(
host,
port,
sourceAddress: sourceAddress,
sourcePort: sourcePort,
timeout: timeout,
);
}
/// Like [connect], but returns a [Future] that completes with a
/// [ConnectionTask] that can be cancelled if the [Socket] is no
/// longer needed.
static Future<ConnectionTask<Socket>> startConnect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
}) {
final IOOverrides? overrides = IOOverrides.current;
if (overrides == null) {
return Socket._startConnect(
host,
port,
sourceAddress: sourceAddress,
sourcePort: sourcePort,
);
}
return overrides.socketStartConnect(
host,
port,
sourceAddress: sourceAddress,
sourcePort: sourcePort,
);
}
external static Future<Socket> _connect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
Duration? timeout,
});
external static Future<ConnectionTask<Socket>> _startConnect(
host,
int port, {
sourceAddress,
int sourcePort = 0,
});
Future<List<Object?>> _detachRaw();
/// Destroys the socket in both directions.
///
/// Calling [destroy] will make the send a close event on the stream
/// and will no longer react on data being piped to it.
///
/// Call [close] (inherited from [IOSink]) to only close the [Socket]
/// for sending data.
void destroy();
/// Customizes the [RawSocket].
///
/// See [SocketOption] for available options.
///
/// Returns `true` if the option was set successfully, false otherwise.
///
/// Throws a [SocketException] if the socket has been destroyed or upgraded to
/// a secure socket.
bool setOption(SocketOption option, bool enabled);
/// Reads low level information about the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Returns the [RawSocketOption.value] on success.
///
/// Throws an [OSError] on failure and a [SocketException] if the socket has
/// been destroyed or upgraded to a secure socket.
Uint8List getRawOption(RawSocketOption option);
/// Customizes the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Throws an [OSError] on failure and a [SocketException] if the socket has
/// been destroyed or upgraded to a secure socket.
void setRawOption(RawSocketOption option);
/// Unsupported operation on sockets.
///
/// This method, which is inherited from [IOSink], is not supported on
/// sockets, and **must not** be called.
/// Sockets have no way to report errors, so any error passed in to
/// a socket using [addError] would be lost.
void addError(Object error, [StackTrace? stackTrace]);
/// The port used by this socket.
///
/// Throws a [SocketException] if the socket is closed.
/// The port is 0 if the socket is a Unix domain socket.
int get port;
/// The remote port connected to by this socket.
///
/// Throws a [SocketException] if the socket is closed.
/// The port is 0 if the socket is a Unix domain socket.
int get remotePort;
/// The [InternetAddress] used to connect this socket.
///
/// Throws a [SocketException] if the socket is closed.
InternetAddress get address;
/// The remote [InternetAddress] connected to by this socket.
///
/// Throws a [SocketException] if the socket is closed.
InternetAddress get remoteAddress;
Future close();
Future get done;
}
/// A data packet received by a [RawDatagramSocket].
final class Datagram {
/// The actual bytes of the message.
Uint8List data;
/// The address of the socket which sends the data.
InternetAddress address;
/// The port of the socket which sends the data.
int port;
Datagram(this.data, this.address, this.port);
}
/// A wrapper around OS resource handle so it can be passed via Socket
/// as part of [SocketMessage].
abstract interface class ResourceHandle {
/// Creates wrapper around opened file.
external factory ResourceHandle.fromFile(RandomAccessFile file);
/// Creates wrapper around opened socket.
external factory ResourceHandle.fromSocket(Socket socket);
/// Creates wrapper around opened raw socket.
external factory ResourceHandle.fromRawSocket(RawSocket socket);
/// Creates wrapper around opened raw datagram socket.
external factory ResourceHandle.fromRawDatagramSocket(
RawDatagramSocket socket,
);
/// Creates wrapper around current stdin.
external factory ResourceHandle.fromStdin(Stdin stdin);
/// Creates wrapper around current stdout.
external factory ResourceHandle.fromStdout(Stdout stdout);
// Creates wrapper around a readable pipe.
external factory ResourceHandle.fromReadPipe(ReadPipe pipe);
// Creates wrapper around a writeable pipe.
external factory ResourceHandle.fromWritePipe(WritePipe pipe);
/// Extracts opened file from resource handle.
///
/// This can also be used when receiving stdin and stdout handles and read
/// and write pipes.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
///
/// If this resource handle is not a file or stdio handle, the behavior of the
/// returned [RandomAccessFile] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
RandomAccessFile toFile();
/// Extracts opened socket from resource handle.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
//
/// If this resource handle is not a socket handle, the behavior of the
/// returned [Socket] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
Socket toSocket();
/// Extracts opened raw socket from resource handle.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
///
/// If this resource handle is not a socket handle, the behavior of the
/// returned [RawSocket] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
RawSocket toRawSocket();
/// Extracts opened raw datagram socket from resource handle.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
///
/// If this resource handle is not a datagram socket handle, the behavior of
/// the returned [RawDatagramSocket] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
RawDatagramSocket toRawDatagramSocket();
/// Extracts a read pipe from resource handle.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
///
/// If this resource handle is not a readable pipe, the behavior of the
/// returned [ReadPipe] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
ReadPipe toReadPipe();
/// Extracts a write pipe from resource handle.
///
/// Since the [ResourceHandle] represents a single OS resource,
/// none of [toFile], [toSocket], [toRawSocket], or [toRawDatagramSocket],
/// [toReadPipe], [toWritePipe], can be called after a call to this method.
///
/// If this resource handle is not a writeable pipe, the behavior of the
/// returned [ReadPipe] is completely unspecified.
/// Be very careful to avoid using a handle incorrectly.
WritePipe toWritePipe();
}
/// Control message part of the [SocketMessage] received by a call to
/// [RawSocket.readMessage].
///
/// Control messages could carry different information including
/// [ResourceHandle]. If [ResourceHandle]s are available as part of this message,
/// they can be extracted via [extractHandles].
abstract interface class SocketControlMessage {
/// Creates a control message containing the provided [handles].
///
/// This is used by the sender when it sends handles across the socket.
/// Receiver can extract the handles from the message using [extractHandles].
external factory SocketControlMessage.fromHandles(
List<ResourceHandle> handles,
);
/// Extracts the list of handles embedded in this message.
///
/// This method must only be used to extract handles from messages
/// received on a socket. It must not be used on a socket control
/// message that is created locally, and has not been sent using
/// [RawSocket.sendMessage].
///
/// This method must only be called once.
/// Calling it multiple times may cause duplicated handles with unspecified
/// behavior.
List<ResourceHandle> extractHandles();
/// A platform specific value used to determine the kind of control message.
///
/// Together with [type], these two integers identify the kind of control
/// message in a platform specific way.
/// For example, on Linux certain combinations of these values indicate
/// that this is a control message that carries [ResourceHandle]s.
int get level;
/// A platform specific value used to determine the kind of control message.
///
/// Together with [level], these two integers identify the kind of control
/// message in a platform specific way.
/// For example, on Linux certain combinations of these values indicate
/// that this is a control message that carries [ResourceHandle]s.
int get type;
/// Actual bytes that were passed as part of the control message by the
/// underlying platform.
///
/// The bytes are interpreted differently depending on the [level] and
/// [type]. These actual bytes can be used to inspect and interpret
/// non-handle-carrying messages.
Uint8List get data;
}
/// A socket message received by a [RawDatagramSocket].
///
/// A socket message consists of [data] bytes and [controlMessages].
final class SocketMessage {
/// The actual bytes of the message.
final Uint8List data;
/// The control messages sent as part of this socket message.
///
/// This list can be empty.
final List<SocketControlMessage> controlMessages;
SocketMessage(this.data, this.controlMessages);
}
/// An unbuffered interface to a UDP socket.
///
/// The raw datagram socket delivers a [Stream] of [RawSocketEvent]s in the
/// same chunks as the underlying operating system receives them.
///
/// Note that the event [RawSocketEvent.readClosed] will never be
/// received as an UDP socket cannot be closed by a remote peer.
///
/// It is not the same as a
/// [POSIX raw socket](http://man7.org/linux/man-pages/man7/raw.7.html).
///
/// ```dart
/// import 'dart:io';
/// import 'dart:typed_data';
///
/// void main() async {
/// // Read the current time from an NTP server.
/// final serverAddress = (await InternetAddress.lookup('pool.ntp.org')).first;
/// final clientSocket = await RawDatagramSocket.bind(
/// serverAddress.type == InternetAddressType.IPv6
/// ? InternetAddress.anyIPv6
/// : InternetAddress.anyIPv4,
/// 0);
/// final ntpQuery = Uint8List(48);
/// ntpQuery[0] = 0x23; // See RFC 5905 7.3
///
/// clientSocket.listen((event) {
/// switch (event) {
/// case RawSocketEvent.read:
/// final datagram = clientSocket.receive();
/// // Parse `datagram.data`
/// clientSocket.close();
/// break;
/// case RawSocketEvent.write:
/// if (clientSocket.send(ntpQuery, serverAddress, 123) > 0) {
/// clientSocket.writeEventsEnabled = false;
/// }
/// break;
/// case RawSocketEvent.closed:
/// break;
/// default:
/// throw "Unexpected event $event";
/// }
/// });
/// }
/// ```
abstract interface class RawDatagramSocket extends Stream<RawSocketEvent> {
/// Whether the [RawDatagramSocket] should listen for
/// [RawSocketEvent.read] events.
///
/// Default is `true`.
abstract bool readEventsEnabled;
/// Whether the [RawDatagramSocket] should listen for
/// [RawSocketEvent.write] events.
///
/// Default is `true`.
/// This is a one-shot listener, and [writeEventsEnabled] must be set to true
/// again to receive another write event.
abstract bool writeEventsEnabled;
/// Whether multicast traffic is looped back to the host.
///
/// By default multicast loopback is enabled.
abstract bool multicastLoopback;
/// The maximum network hops for multicast packages
/// originating from this socket.
///
/// For IPv4 this is referred to as TTL (time to live).
///
/// By default this value is 1 causing multicast traffic to stay on
/// the local network.
abstract int multicastHops;
/// The network interface used for outgoing multicast packages.
///
/// A value of `null` indicate that the system chooses the network
/// interface to use.
///
/// By default this value is `null`
@Deprecated(
"This property is not implemented. Use getRawOption and "
"setRawOption instead.",
)
NetworkInterface? multicastInterface;
/// Whether IPv4 broadcast is enabled.
///
/// IPv4 broadcast needs to be enabled by the sender for sending IPv4
/// broadcast packages. By default IPv4 broadcast is disabled.
///
/// For IPv6 there is no general broadcast mechanism. Use multicast
/// instead.
abstract bool broadcastEnabled;
/// Binds a socket to the given [host] and [port].
///
/// When the socket is bound and has started listening on [port], the returned
/// future completes with the [RawDatagramSocket] of the bound socket.
///
/// The [host] can either be a [String] or an [InternetAddress]. If [host] is a
/// [String], [bind] will perform a [InternetAddress.lookup] and use the first
/// value in the list. To listen on the loopback interface, which will allow
/// only incoming connections from the local host, use the value
/// [InternetAddress.loopbackIPv4] or [InternetAddress.loopbackIPv6].
/// To allow for incoming connection from any network use either one of
/// the values [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
/// bind to all interfaces, or use the IP address of a specific interface.
///
/// The [reuseAddress] should be set for all listeners that bind to the same
/// address. Otherwise, it will fail with a [SocketException].
///
/// The [reusePort] specifies whether the port can be reused.
///
/// The [ttl] sets `time to live` of a datagram sent on the socket.
external static Future<RawDatagramSocket> bind(
host,
int port, {
bool reuseAddress = true,
bool reusePort = false,
int ttl = 1,
});
/// The port used by this socket.
int get port;
/// The address used by this socket.
InternetAddress get address;
/// Closes the datagram socket.
void close();
/// Asynchronously sends a datagram.
///
/// Returns the number of bytes written. This will always be either
/// the size of [buffer] or `0`.
///
/// A return value of `0` indicates that sending the datagram would block and
/// that the [send] call can be tried again.
///
/// A return value of the size of [buffer] indicates that a request to
/// transmit the datagram was made to the operating system. It does not
/// indicate that the operating system successfully sent the datagram. If a
/// local failure to send the datagram occurs then an error event will be
/// added to the [Stream]. If a networking or remote failure occurs then it
/// will not be reported.
///
/// The maximum size of a IPv4 UDP datagram is 65535 bytes (including both
/// data and headers) but the practical maximum size is likely to be much
/// lower due to operating system limits and the network's maximum
/// transmission unit (MTU).
///
/// Some IPv6 implementations may support payloads up to 4GB (see RFC-2675)
/// but that support is limited (see RFC-6434) and has been removed in later
/// standards (see RFC-8504).
///
/// [Emperical testing by the Chromium team](https://groups.google.com/a/chromium.org/g/proto-quic/c/uKWLRh9JPCo)
/// suggests that payloads later than 1350 cannot be reliably received.
int send(List<int> buffer, InternetAddress address, int port);
/// Receives a datagram.
///
/// Returns `null` if there are no datagrams available.
Datagram? receive();
/// Joins a multicast group.
///
/// If an error occur when trying to join the multicast group, an
/// exception is thrown.
void joinMulticast(InternetAddress group, [NetworkInterface? interface]);
/// Leaves a multicast group.
///
/// If an error occur when trying to join the multicast group, an
/// exception is thrown.
void leaveMulticast(InternetAddress group, [NetworkInterface? interface]);
/// Reads low level information about the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Returns [RawSocketOption.value] on success.
///
/// Throws an [OSError] on failure.
Uint8List getRawOption(RawSocketOption option);
/// Customizes the [RawSocket].
///
/// See [RawSocketOption] for available options.
///
/// Throws an [OSError] on failure.
void setRawOption(RawSocketOption option);
}
/// Exception thrown when a socket operation fails.
class SocketException implements IOException {
/// Description of the error.
final String message;
/// The underlying OS error.
///
/// If this exception is not thrown due to an OS error, the value is `null`.
final OSError? osError;
/// The address of the socket giving rise to the exception.
///
/// This is either the source or destination address of a socket,
/// or it can be `null` if no socket end-point was involved in the cause of
/// the exception.
final InternetAddress? address;
/// The port of the socket giving rise to the exception.
///
/// This is either the source or destination address of a socket,
/// or it can be `null` if no socket end-point was involved in the cause of
/// the exception.
final int? port;
/// Creates a [SocketException] with the provided values.
const SocketException(this.message, {this.osError, this.address, this.port});
/// Creates an exception reporting that a socket was used after it was closed.
const SocketException.closed()
: message = 'Socket has been closed',
osError = null,
address = null,
port = null;
String toString() {
StringBuffer sb = StringBuffer();
sb.write("SocketException");
if (message.isNotEmpty) {
sb.write(": $message");
if (osError != null) {
sb.write(" ($osError)");
}
} else if (osError != null) {
sb.write(": $osError");
}
if (address != null) {
sb.write(", address = ${address!.host}");
}
if (port != null) {
sb.write(", port = $port");
}
return sb.toString();
}
}