circuitpython: Socket closing if timed out and no data on socket

If no data is received on a socket, and the socket has timed out, a socket will close. Subsequent reads raise OSError: [Errno 9] EBADF.

I dug deeper, this behavior is expected (https://github.com/adafruit/circuitpython/blob/main/ports/esp32s2/common-hal/socketpool/Socket.c#L123) on the esp32-s2.

However, closing the socket immediately makes it difficult to poll a socket to check if data is available.

For example, polling a socket for a response, like in MiniMQTT (https://github.com/brentru/Adafruit_CircuitPython_MiniMQTT/blob/cpython-s2/adafruit_minimqtt/adafruit_minimqtt.py#L872), raises OSError instead of simply timing out and leaving the socket open.

0
bytearray(b'\n')
recv from socket...
Traceback (most recent call last):
  File "code.py", line 47, in <module>
OSError: [Errno 9] EBADF

Could we instead raise a timeout or perform some other behavior? CPython raises a socket.timeout (https://docs.python.org/3.8/library/socket.html#socket.timeout) error for this operation and leaves the socket open.


Code example, , receiving one byte of code at a time from a server, until it reads 0:

import time
import ssl
import socketpool
import wifi
import board
import neopixel

# Add a secrets.py to your filesystem that has a dictionary called secrets with "ssid" and
# "password" keys with your WiFi credentials. DO NOT share that file or commit it into Git or other
# source control.
# pylint: disable=no-name-in-module,wrong-import-order
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

print("Connecting to %s"%secrets["ssid"])
wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!"%secrets["ssid"])

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

# Host
host = "0.tcp.ngrok.io"
port = 10480

addr_info = pool.getaddrinfo(
    host, port, 0, pool.SOCK_STREAM
)[0]

sock = pool.socket(
    addr_info[0], addr_info[1], addr_info[2]
)

sock.connect((host, port))
sock.settimeout(1)

buf = bytearray(1)
# attempt to recv 
while True:
    print("recv from socket...")
    time.sleep(2)
    data = sock.recv_into(buf, 1)
    print(data)
    print(buf)

Then I set up a simple tcp packet server using netcat (nc -l 23999)

Output:

Connected to!
recv from socket...
1
bytearray(b'h')
recv from socket...
1
bytearray(b'e')
recv from socket...
1
bytearray(b'l')
recv from socket...
1
bytearray(b'l')
recv from socket...
1
bytearray(b'o')
recv from socket...
1
bytearray(b'\n')
recv from socket...
0
bytearray(b'\n')
recv from socket...
Traceback (most recent call last):
  File "code.py", line 47, in <module>
OSError: [Errno 9] EBADF

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (8 by maintainers)

Most upvoted comments

@hierophect please chat with @tannewt and @brentru, im not the one using the code 😃