Tôi gặp vấn đề với các giải pháp được đề xuất, việc sử dụng lookup
không phải lúc nào cũng trả lại giá trị mong đợi.
Điều này là do bộ nhớ đệm DNS, giá trị của cuộc gọi được lưu vào bộ nhớ cache và nếu thực hiện một cuộc gọi thích hợp vào lần thử tiếp theo, nó sẽ trả lại giá trị được lưu trong bộ nhớ cache. Tất nhiên đây là một vấn đề ở đây vì nó có nghĩa là nếu bạn mất kết nối và gọi lookup
nó vẫn có thể trả về giá trị được lưu trong bộ nhớ cache như khi bạn có internet và ngược lại, nếu bạn kết nối lại internet sau khi lookup
trả về null, nó sẽ vẫn trả về null trong khoảng thời gian bộ nhớ cache, có thể mất vài phút, ngay cả khi bạn có Internet ngay bây giờ.
TL; DR: lookup
trả lại một thứ gì đó không nhất thiết có nghĩa là bạn có internet và nó không trả lại bất cứ thứ gì không nhất thiết có nghĩa là bạn không có internet. Nó không đáng tin cậy.
Tôi đã triển khai giải pháp sau bằng cách lấy cảm hứng từ 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;
}
Lệnh gọi _checkInternetAccess
cần tối đa một khoảng thời gian timeout
để hoàn thành (ở đây là 3 giây) và nếu chúng tôi có thể truy cập bất kỳ DNS nào, nó sẽ hoàn tất ngay sau khi đạt đến DNS đầu tiên, mà không cần đợi những DNS khác (vì chỉ cần đạt được một DNS là đủ biết bạn có internet). Tất cả các cuộc gọi đến _pingDns
được thực hiện song song.
Nó có vẻ hoạt động tốt trên mạng IPV4 và khi tôi không thể kiểm tra nó trên mạng IPV6 (tôi không có quyền truy cập vào mạng này), tôi nghĩ nó vẫn sẽ hoạt động. Nó cũng hoạt động trên các bản dựng chế độ phát hành, nhưng tôi vẫn phải gửi ứng dụng của mình cho Apple để xem họ có tìm thấy bất kỳ vấn đề nào với giải pháp này hay không.
Nó cũng sẽ hoạt động ở hầu hết các quốc gia (bao gồm cả Trung Quốc), nếu nó không hoạt động ở một quốc gia, bạn có thể thêm DNS vào danh sách có thể truy cập từ quốc gia mục tiêu của bạn.