ping method

Future<bool> ping({
  1. Duration timeout = const Duration(seconds: 5),
})

Send a PING frame and wait for ACK to verify connection liveness. Returns true if ACK received within timeout, false otherwise.

This is a lightweight, non-intrusive way to check if the connection is still alive. The PING frame is just 1 byte and elicits an ACK response.

Implementation

Future<bool> ping({Duration timeout = const Duration(seconds: 5)}) async {
  if (_closing || closing) return false;

  // Send a packet containing just a PING frame
  final pingSequence = _packetManager.nextSequence;
  final packet = UDXPacket(
    destinationCid: cids.remoteCid,
    sourceCid: cids.localCid,
    destinationStreamId: 0, // Connection-level packet
    sourceStreamId: 0,       // Connection-level packet
    sequence: pingSequence,
    frames: [PingFrame()],
  );

  final completer = Completer<bool>();
  Timer? timeoutTimer;
  StreamSubscription? ackSubscription;

  // Listen for ACK of our ping packet
  ackSubscription = on('ack').listen((event) {
    // Check if this ACK acknowledges our ping packet
    if (event.data is Map) {
      final data = event.data as Map;
      if (data['largestAcked'] != null) {
        final largestAcked = data['largestAcked'] as int;
        if (largestAcked >= pingSequence) {
          // Our ping was acknowledged
          if (!completer.isCompleted) {
            timeoutTimer?.cancel();
            ackSubscription?.cancel();
            completer.complete(true);
          }
        }
      }
    }
  });

  // Set up timeout
  timeoutTimer = Timer(timeout, () {
    if (!completer.isCompleted) {
      ackSubscription?.cancel();
      completer.complete(false);
    }
  });

  // Send the ping packet
  try {
    send(packet.toBytes());
  } catch (e) {
    timeoutTimer.cancel();
    ackSubscription.cancel();
    if (!completer.isCompleted) {
      completer.complete(false);
    }
  }

  return completer.future;
}