Nonblocking way to detect internet connectivity?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Nonblocking way to detect internet connectivity?

Post by pythoncoder » Wed Aug 16, 2017 6:01 am

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.
Peter Hinch
Index to my micropython libraries.

pfalcon
Posts: 1155
Joined: Fri Feb 28, 2014 2:05 pm

Re: Nonblocking way to detect internet connectivity?

Post by pfalcon » Sun Aug 20, 2017 2:31 pm

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.
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/

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Nonblocking way to detect internet connectivity?

Post by pythoncoder » Wed Aug 23, 2017 6:36 am

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.

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
The full code will be in https://github.com/peterhinch/micropyth ... mqtt_as.py soon.
Peter Hinch
Index to my micropython libraries.

cowbjt
Posts: 3
Joined: Fri Apr 27, 2018 1:19 am

Re: Nonblocking way to detect internet connectivity?

Post by cowbjt » Fri Apr 27, 2018 1:39 am

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]

cowbjt
Posts: 3
Joined: Fri Apr 27, 2018 1:19 am

Re: Nonblocking way to detect internet connectivity?

Post by cowbjt » Mon Apr 30, 2018 2:06 am

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.

Post Reply