Miałem problem z proponowanymi rozwiązaniami, użycie lookup
nie zawsze zwraca oczekiwaną wartość.
Dzieje się tak z powodu buforowania DNS, wartość wywołania jest zapisywana w pamięci podręcznej i nie ma konieczności wykonania prawidłowego wywołania przy następnej próbie, która zwraca wartość z pamięci podręcznej. Oczywiście jest to problem, ponieważ oznacza to, że jeśli utracisz łączność i zadzwonisz lookup
, nadal możesz zwrócić wartość z pamięci podręcznej, tak jakbyś miał Internet, i odwrotnie, jeśli ponownie połączysz się z Internetem po lookup
zwróceniu wartości null, nadal zwróci wartość null przez czas trwania pamięci podręcznej, co może zająć kilka minut, nawet jeśli masz teraz internet.
TL; DR: lookup
zwrócenie czegoś niekoniecznie oznacza, że masz internet, a brak zwrotu niekoniecznie oznacza, że nie masz internetu. To nie jest wiarygodne.
Wdrożyłem następujące rozwiązanie, czerpiąc inspirację z data_connection_checker
wtyczki:
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;
}
Wywołanie do _checkInternetAccess
trwa co najwyżej timeout
(3 sekundy tutaj), a jeśli uda nam się dotrzeć do któregokolwiek z DNS, zakończy się, gdy tylko zostanie osiągnięty pierwszy, bez czekania na pozostałe (ponieważ dotarcie do jednego wystarczy, aby wiem, że masz internet). Wszystkie wywołania _pingDns
są wykonywane równolegle.
Wygląda na to, że działa dobrze w sieci IPV4, a kiedy nie mogę go przetestować w sieci IPV6 (nie mam do niej dostępu), myślę, że powinien nadal działać. Działa również w kompilacjach w trybie wydania, ale muszę jeszcze przesłać moją aplikację do Apple, aby sprawdzić, czy znajdą jakiś problem z tym rozwiązaniem.
Powinien również działać w większości krajów (w tym w Chinach), jeśli nie działa w jednym, możesz dodać DNS do listy, która jest dostępna z kraju docelowego.