Ho avuto un problema con le soluzioni proposte, l'utilizzo lookup
non sempre restituisce il valore atteso.
Ciò è dovuto alla memorizzazione nella cache DNS, il valore della chiamata viene memorizzato nella cache e invece di eseguire una chiamata corretta al tentativo successivo restituisce il valore memorizzato nella cache. Ovviamente questo è un problema qui in quanto significa che se si perde la connettività e si chiama lookup
potrebbe comunque restituire il valore memorizzato nella cache come se si avesse Internet e, viceversa, se si ricollega Internet dopo aver lookup
restituito null, restituirà comunque null per la durata del cache, che può durare alcuni minuti, anche se ora hai Internet.
TL; DR: lookup
restituire qualcosa non significa necessariamente che hai Internet e non restituire nulla non significa necessariamente che non hai Internet. Non è affidabile.
Ho implementato la seguente soluzione ispirandomi al data_connection_checker
plugin:
Future<bool> _checkInternetAccess() {
final List<InternetAddress> dnss = [
InternetAddress('8.8.8.8', type: InternetAddressType.IPv4),
InternetAddress('2001:4860:4860::8888', type: InternetAddressType.IPv6),
InternetAddress('1.1.1.1', type: InternetAddressType.IPv4),
InternetAddress('2606:4700:4700::1111', type: InternetAddressType.IPv6),
InternetAddress('208.67.222.222', type: InternetAddressType.IPv4),
InternetAddress('2620:0:ccc::2', type: InternetAddressType.IPv6),
InternetAddress('180.76.76.76', type: InternetAddressType.IPv4),
InternetAddress('2400:da00::6666', type: InternetAddressType.IPv6),
];
final Completer<bool> completer = Completer<bool>();
int callsReturned = 0;
void onCallReturned(bool isAlive) {
if (completer.isCompleted) return;
if (isAlive) {
completer.complete(true);
} else {
callsReturned++;
if (callsReturned >= dnss.length) {
completer.complete(false);
}
}
}
dnss.forEach((dns) => _pingDns(dns).then(onCallReturned));
return completer.future;
}
Future<bool> _pingDns(InternetAddress dnsAddress) async {
const int dnsPort = 53;
const Duration timeout = Duration(seconds: 3);
Socket socket;
try {
socket = await Socket.connect(dnsAddress, dnsPort, timeout: timeout);
socket?.destroy();
return true;
} on SocketException {
socket?.destroy();
}
return false;
}
La chiamata a _checkInternetAccess
richiede al massimo una durata di timeout
completamento (3 secondi qui), e se riusciamo a raggiungere uno qualsiasi dei DNS si completerà non appena verrà raggiunto il primo, senza attendere gli altri (poiché raggiungerne uno è sufficiente per sai di avere internet). Tutte le chiamate a _pingDns
vengono effettuate in parallelo.
Sembra funzionare bene su una rete IPV4 e quando non riesco a testarlo su una rete IPV6 (non ho accesso a una rete) penso che dovrebbe funzionare ancora. Funziona anche su build in modalità di rilascio, ma devo ancora inviare la mia app ad Apple per vedere se trovano problemi con questa soluzione.
Dovrebbe funzionare anche nella maggior parte dei paesi (compresa la Cina), se non funziona in uno puoi aggiungere un DNS all'elenco accessibile dal tuo paese di destinazione.