Tuve un problema con las soluciones propuestas, el uso lookup
no siempre devuelve el valor esperado.
Esto se debe al almacenamiento en caché de DNS, el valor de la llamada se almacena en caché y, en lugar de realizar una llamada adecuada en el próximo intento, devuelve el valor en caché. Por supuesto, esto es un problema aquí, ya que significa que si pierde la conectividad y lo llama lookup
, aún podría devolver el valor en caché como si tuviera Internet y, a la inversa, si vuelve a conectar su Internet después de lookup
devolver un valor nulo, seguirá devolviendo un valor nulo durante la duración del caché, que puede tardar unos minutos, incluso si tiene Internet ahora.
TL; DR: lookup
devolver algo no significa necesariamente que tenga Internet, y no devolver nada no significa necesariamente que no tenga Internet. No es confiable.
Implementé la siguiente solución inspirándome en el data_connection_checker
complemento:
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 llamada a _checkInternetAccess
tarda como máximo una duración de timeout
en completarse (3 segundos aquí), y si podemos llegar a alguno de los DNS se completará en cuanto se alcance el primero, sin esperar a los demás (ya que alcanzar uno es suficiente para sabes que tienes internet). Todas las llamadas a _pingDns
se realizan en paralelo.
Parece funcionar bien en una red IPV4, y cuando no puedo probarlo en una red IPV6 (no tengo acceso a una), creo que debería funcionar. También funciona en compilaciones en modo de lanzamiento, pero todavía tengo que enviar mi aplicación a Apple para ver si encuentran algún problema con esta solución.
También debería funcionar en la mayoría de los países (incluida China); si no funciona en uno, puede agregar un DNS a la lista que sea accesible desde su país de destino.