socket.settimeout() not working

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
Romik
Posts: 11
Joined: Mon Dec 09, 2019 2:40 pm

socket.settimeout() not working

Post by Romik » Wed Feb 05, 2020 11:27 am

PyBoard 1.1 + Ethernet

Code: Select all

>>> dir(socket.socket)
['__class__', '__name__', 'close', 'send', '__bases__', '__del__', 'accept', 'bind', 'connect', 'listen', 'recv', 'recvfrom', 'sendto', 'setblocking',
 'setsockopt', 'settimeout']

>>> dir(socket.socket.settimeout)
['__class__']

>>> host = '10.128.0.148'
>>> NTP_QUERY = bytearray(48)
>>> NTP_QUERY[0] = 0x1b
>>> addr = socket.getaddrinfo(host,123)[0][-1]
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.settimeout(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 107] ENOTCONN

Why is this happening and how can this be fixed.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: socket.settimeout() not working

Post by jimmo » Thu Feb 06, 2020 1:43 am

The socket has to be connected before you can set the timeout. Looking at the code, the reason for this is that the timeout is handled by the NIC layer, and until you're connected (or bound), the socket doesn't know which NIC it's using. (This is when you're relying on the NIC to run the TCP stack for you, i.e. using an external wiznet or something). It's a bit different when you're using LWIP (e.g. PYBD with WiFi).

I don't have wiznet to test this with (I assume that's what you're using?) but I think possibly if you bind the socket first to the local address first then it might work?

Romik
Posts: 11
Joined: Mon Dec 09, 2019 2:40 pm

Re: socket.settimeout() not working

Post by Romik » Thu Feb 06, 2020 7:36 am

All is working fine if host is available..
if the host is not available, msg = s.recvfrom (48) hangs and does not exit.

Code: Select all

nic = WIZNET5K(SPI(1),Pin.board.X12,Pin.board.X11)
ip = ('10.128.0.206','255.255.255.0','10.128.0.1','8.8.8.8')
nic.ifconfig(ip)

host = 'pool.ntp.org'

addr = socket.getaddrinfo(host,123)[0][-1]
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)               
# s.settimeout(5)
res = s.sendto(NTP_QUERY, addr)        
msg = s.recvfrom(48)
s.close()

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: socket.settimeout() not working

Post by jimmo » Thu Feb 06, 2020 9:38 am

Try setting the timeout after the sendto.

The recvfrom will block unless there's a timeout set. To set a timeout you need the socket to be bound to a NIC. To do so you either need to connect(), bind(), or sendto().

Romik
Posts: 11
Joined: Mon Dec 09, 2019 2:40 pm

Re: socket.settimeout() not working

Post by Romik » Thu Feb 06, 2020 10:48 am

The error has changed: OSError: [Errno 22] EINVAL

Code: Select all

>>> res = s.sendto(NTP_QUERY, addr)
>>> s.settimeout(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 22] EINVAL

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: socket.settimeout() not working

Post by jimmo » Thu Feb 06, 2020 10:52 am

Ahh, I just read more of the code. The WizNet driver doesn't implement settimeout:

from ports/stm32/modnwwiznet5k.c

Code: Select all

STATIC int wiznet5k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t timeout_ms, int *_errno) {
    // TODO
    *_errno = MP_EINVAL;
    return -1;

    /*
    if (timeout_ms == 0) {
        // set non-blocking mode
        uint8_t arg = SOCK_IO_NONBLOCK;
        WIZCHIP_EXPORT(ctlsocket)(socket->u_param.fileno, CS_SET_IOMODE, &arg);
    }
    */
}
It wasn't getting to here before because modsocket requires that it has a NIC (hence the original ENOTCONN error), but yeah, the NIC driver doesn't implement this.

Romik
Posts: 11
Joined: Mon Dec 09, 2019 2:40 pm

Re: socket.settimeout() not working

Post by Romik » Thu Feb 06, 2020 10:57 am

Thank you. :(

Romik
Posts: 11
Joined: Mon Dec 09, 2019 2:40 pm

Re: socket.settimeout() not working

Post by Romik » Thu Feb 06, 2020 11:40 am

I found in documentation. Sorry. All is working.

Code: Select all

# Instead of:
s.settimeout(1.0)  # time in seconds
s.read(10)  # may timeout

# Use:
poller = uselect.poll()
poller.register(s, uselect.POLLIN)
res = poller.poll(1000)  # time in milliseconds
if not res:
    # s is still not ready for input, i.e. operation timed out
My code:

Code: Select all

   addr = socket.getaddrinfo(host,123)[0][-1]
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    res = s.sendto(NTP_QUERY, addr)     
    poller = uselect.poll() 
    poller.register(s, uselect.POLLIN) 
    res = poller.poll(1000)    
    if not res:
        return 0
    else:
        msg = s.recvfrom(48)
    s.close()            

Post Reply