ping method
Sends a PING under a fresh sequence and waits for its acknowledgement:
true if the peer answered within timeout. Both peers acknowledge a
numbered PING at once. The packet is tracked until acknowledged or the
wait ends, so its ACK is matched (and gives an RTT sample); it is never
retransmitted. A later packet's acknowledgement also answers, since it
proves the same thing.
The PING used to be sent untracked, so on a connection with nothing else in flight its ACK matched nothing and this always timed out.
Implementation
Future<bool> ping({Duration timeout = const Duration(seconds: 5)}) async {
if (_closing || closing) return false;
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;
void finish(bool answered) {
if (completer.isCompleted) return;
timeoutTimer?.cancel();
ackSubscription?.cancel();
_packetManager.forget(pingSequence);
completer.complete(answered);
}
ackSubscription = on('ack').listen((event) {
final data = event.data;
if (data is Map && data['largestAcked'] is int && (data['largestAcked'] as int) >= pingSequence) {
finish(true);
}
});
timeoutTimer = clock.timer(timeout, () => finish(false));
_packetManager.trackUntilAcked(packet);
try {
send(packet.toBytes());
} catch (e) {
finish(false);
}
return completer.future;
}