socket.getaddrinfo() blocks for about 15 seconds if the internet is down. My application uses uasyncio so this sucks.
Is there a way to determine whether connectivity is available before executing this call? Or (even better) a nonblocking implementation of socket.getaddrinfo() which triggers the request and allows you to wait on the response. A timeout could then detect failure.
[EDIT]
I've thought of a solution specific to my application but I'd like to hear any ideas on a general approach.
Nonblocking way to detect internet connectivity?
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Nonblocking way to detect internet connectivity?
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Nonblocking way to detect internet connectivity?
For uasyncio, async DNS resolver would need to be written (pretty cute/useful task btw).
Otherwise, there's no way to test "Internet connectivity", as "Internet" is not a specific, "countable", term. However, you can invent any number of heuristics for that. For example, if I faced such an issue, I'd likely either try to open a connection to a specific site of the interest, or if really targetting "Internet", then would send a UDP datagram to some well-known server on the net and see if I get a reply. Google's DNS servers like 8.8.8.8 seem just right for that, and may be first step towards implementing the aforementioned async DNS resolver.
Otherwise, there's no way to test "Internet connectivity", as "Internet" is not a specific, "countable", term. However, you can invent any number of heuristics for that. For example, if I faced such an issue, I'd likely either try to open a connection to a specific site of the interest, or if really targetting "Internet", then would send a UDP datagram to some well-known server on the net and see if I get a reply. Google's DNS servers like 8.8.8.8 seem just right for that, and may be first step towards implementing the aforementioned async DNS resolver.
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: Nonblocking way to detect internet connectivity?
Thanks for that, Paul. Great idea.
For anyone wanting to do something similar the following gives the gist of how I implemented this. Note that the _as_read() and _as_write() methods do asynchronous reads and writes to a socket - in each case a timeout raises OSError(-1). The isconnected() method performs a basic check on WiFi connectivity.
The full code will be in https://github.com/peterhinch/micropyth ... mqtt_as.py soon.
For anyone wanting to do something similar the following gives the gist of how I implemented this. Note that the _as_read() and _as_write() methods do asynchronous reads and writes to a socket - in each case a timeout raises OSError(-1). The isconnected() method performs a basic check on WiFi connectivity.
Code: Select all
# Check internet connectivity by sending DNS lookup to Google's 8.8.8.8
async def wan_ok(self, packet = b'$\x1a\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x06google\x03com\x00\x00\x01\x00\x01'):
if not self.isconnected(): # WiFi is down
return False
length = 32 # DNS query and response packet size
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setblocking(False)
s.connect(('8.8.8.8', 53))
await asyncio.sleep(1)
try:
await self._as_write(packet, sock = s)
await asyncio.sleep(2)
res = await self._as_read(length, s)
if len(res) == length:
return True # DNS response size OK
except OSError: # Timeout on read: no connectivity.
return False
finally:
s.close()
return False
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: Nonblocking way to detect internet connectivity?
Hi pythoncoder,
Thanks for the sample.
I got a weird situation here.
1. I disconnect the RJ45 of my router and keep wifi DNS on.
which means PC and ESP32 could connect to router by wifi but cannot access internet.
2. Run the wan_ok(), socket still can read the DNS response.
3. Ping it on the PC, got a reasonable timeout.
Here is my code that modified from wan_ok:
[code]
def ping(packet=b'$\x1a\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x06google\x03com\x00\x00\x01\x00\x01'):
dns_response_len = 32 # DNS query and response packet size
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
s.connect(('8.8.8.8', 53))
rec = b''
s.write(packet)
await asyncio.sleep_ms(30)
t = ticks_ms()
try:
while len(rec) < dns_response_len:
res = s.read(dns_response_len - len(rec))
if ticks_ms() - t > 1500:
return False
if res is None:
await asyncio.sleep_ms(30)
else:
rec = b''.join((rec, res))
if len(rec) == dns_response_len:
print('google said: {}'.format(rec))
return True
except OSError as e: # Timeout on read: no connectivity.
print('error: {}'.format(str(e)))
return False
finally:
s.close()
return False
[/code]
Thanks for the sample.
I got a weird situation here.
1. I disconnect the RJ45 of my router and keep wifi DNS on.
which means PC and ESP32 could connect to router by wifi but cannot access internet.
2. Run the wan_ok(), socket still can read the DNS response.
3. Ping it on the PC, got a reasonable timeout.
Here is my code that modified from wan_ok:
[code]
def ping(packet=b'$\x1a\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x06google\x03com\x00\x00\x01\x00\x01'):
dns_response_len = 32 # DNS query and response packet size
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
s.connect(('8.8.8.8', 53))
rec = b''
s.write(packet)
await asyncio.sleep_ms(30)
t = ticks_ms()
try:
while len(rec) < dns_response_len:
res = s.read(dns_response_len - len(rec))
if ticks_ms() - t > 1500:
return False
if res is None:
await asyncio.sleep_ms(30)
else:
rec = b''.join((rec, res))
if len(rec) == dns_response_len:
print('google said: {}'.format(rec))
return True
except OSError as e: # Timeout on read: no connectivity.
print('error: {}'.format(str(e)))
return False
finally:
s.close()
return False
[/code]
Re: Nonblocking way to detect internet connectivity?
I found what's going on in that weird situation.
The reply I got is from the router and content is different(but length is the same) between RJ45 plug or unplug.
So, when RJ45 disconnected, the reply might be a fail message.(I didn't check the ICMP spec yet)
P.S.
I tested on other routers, some of them don't send fail message, some of them send fail message.
The reply I got is from the router and content is different(but length is the same) between RJ45 plug or unplug.
So, when RJ45 disconnected, the reply might be a fail message.(I didn't check the ICMP spec yet)
P.S.
I tested on other routers, some of them don't send fail message, some of them send fail message.