From 0682ae7065a7a6b8dc30ea5da049c916b757a87e Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 25 Mar 2026 15:09:11 +0100
Subject: [PATCH] fix: parallel host probing then single connect to winner
---
lib/services/mqtt_service.dart | 70 ++++++++++++++++------------------
1 files changed, 33 insertions(+), 37 deletions(-)
diff --git a/lib/services/mqtt_service.dart b/lib/services/mqtt_service.dart
index 5df0b7b..4e34697 100644
--- a/lib/services/mqtt_service.dart
+++ b/lib/services/mqtt_service.dart
@@ -110,47 +110,43 @@
final clientId = await _getClientId();
- // Connection order: local → cached discovery → Bonjour/scan → VPN → remote
- final attempts = <MapEntry<String, int>>[]; // host → timeout ms
- if (config.localHost != null && config.localHost!.isNotEmpty) {
- attempts.add(MapEntry(config.localHost!, 2500));
- }
- // Try cached discovered host before scanning again
- if (_lastDiscoveredHost != null) {
- attempts.add(MapEntry(_lastDiscoveredHost!, 3000));
- }
- if (config.vpnHost != null && config.vpnHost!.isNotEmpty) {
- attempts.add(MapEntry(config.vpnHost!, 3000));
- }
- if (config.host.isNotEmpty) {
- attempts.add(MapEntry(config.host, 5000));
- }
- _mqttLog('MQTT: attempts=${attempts.map((e) => e.key).join(", ")} port=${config.port}');
+ // Probe all hosts in parallel to find which one responds, then connect to the winner
+ final hosts = <String>{};
+ if (config.localHost != null && config.localHost!.isNotEmpty) hosts.add(config.localHost!);
+ if (_lastDiscoveredHost != null) hosts.add(_lastDiscoveredHost!);
+ if (config.vpnHost != null && config.vpnHost!.isNotEmpty) hosts.add(config.vpnHost!);
+ if (config.host.isNotEmpty) hosts.add(config.host);
+ _mqttLog('MQTT: probing ${hosts.length} hosts in parallel: ${hosts.join(", ")}');
- for (final attempt in attempts) {
- if (_intentionalClose) return;
- _mqttLog('MQTT: trying ${attempt.key}:${config.port}');
- try {
- if (await _tryConnect(attempt.key, clientId, timeout: attempt.value)) return;
- } catch (e) {
- _mqttLog('MQTT: ${attempt.key} error=$e');
+ // Probe all configured hosts in parallel — first to respond wins
+ String? winner;
+ if (hosts.isNotEmpty) {
+ final probes = hosts.map((h) => _probeHost(h, config.port)).toList();
+ // Also start discovery in parallel
+ if (_lastDiscoveredHost == null) {
+ probes.add(() async {
+ final discovered = await _discoverViaMdns();
+ if (discovered != null) {
+ _lastDiscoveredHost = discovered;
+ return discovered;
+ }
+ return null;
+ }());
}
+ final results = await Future.wait(probes);
+ winner = results.firstWhere((r) => r != null, orElse: () => null);
+ } else if (_lastDiscoveredHost == null) {
+ // No configured hosts — try discovery only
+ winner = await _discoverViaMdns();
+ if (winner != null) _lastDiscoveredHost = winner;
}
- // All configured hosts failed — try Bonjour/subnet scan (only once, not on retry)
- if (_lastDiscoveredHost == null && !_intentionalClose) {
- _mqttLog('MQTT: trying Bonjour/subnet discovery...');
- final discovered = await _discoverViaMdns();
- if (discovered != null && !_intentionalClose) {
- _lastDiscoveredHost = discovered;
- _mqttLog('MQTT: discovered $discovered, connecting...');
- try {
- if (await _tryConnect(discovered, clientId, timeout: 3000)) return;
- } catch (e) {
- _mqttLog('MQTT: discovered host $discovered error=$e');
- }
- } else {
- _mqttLog('MQTT: discovery returned nothing');
+ if (winner != null && !_intentionalClose) {
+ _mqttLog('MQTT: probe winner: $winner, connecting...');
+ try {
+ if (await _tryConnect(winner, clientId, timeout: 5000)) return;
+ } catch (e) {
+ _mqttLog('MQTT: connect to $winner failed: $e');
}
}
--
Gitblit v1.3.1