Skip to content

Commit 02e08b3

Browse files
committed
Timeout for send and other fixes
- Socket send now has a timeout so we can eventually release the socket in situations where we are trying to send but the network connection died so there was no TCP handshake to close the connection. - `WSGIServer` now has a 20s timeout for its sockets. - DHCP timeout increased to 30s in accordance with RFC3315. - Recognize more states as "socket is closed and available". - `get_socket` now returns an invalid value if no free socket is available, which will cause an exception in the socket constructor. It used to return 0 if no sockets were available, which is a valid socket value, and caused all kinds of problems for the rightful owner of socket 0.
1 parent a465bb0 commit 02e08b3

File tree

4 files changed

+35
-12
lines changed

4 files changed

+35
-12
lines changed

adafruit_wiznet5k/adafruit_wiznet5k.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132

133133
# Maximum number of sockets to support, differs between chip versions.
134134
W5200_W5500_MAX_SOCK_NUM = const(0x08)
135+
SOCKET_INVALID = const(255)
135136

136137
# UDP socket struct.
137138
UDP_SOCK = {"bytes_remaining": 0, "remote_ip": 0, "remote_port": 0}
@@ -163,7 +164,7 @@ def __init__(
163164
is_dhcp=True,
164165
mac=DEFAULT_MAC,
165166
hostname=None,
166-
dhcp_timeout=3,
167+
dhcp_timeout=30,
167168
debug=False,
168169
):
169170
self._debug = debug
@@ -571,10 +572,16 @@ def get_socket(self):
571572
if self._debug:
572573
print("*** Get socket")
573574

574-
sock = 0
575+
sock = SOCKET_INVALID
575576
for _sock in range(self.max_sockets):
576577
status = self.socket_status(_sock)[0]
577-
if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT):
578+
if status in (
579+
SNSR_SOCK_CLOSED,
580+
SNSR_SOCK_TIME_WAIT,
581+
SNSR_SOCK_FIN_WAIT,
582+
SNSR_SOCK_CLOSE_WAIT,
583+
SNSR_SOCK_CLOSING,
584+
):
578585
sock = _sock
579586
break
580587

@@ -616,7 +623,13 @@ def socket_open(self, socket_num, conn_mode=SNMR_TCP):
616623
if self._debug:
617624
print("*** Opening socket %d" % socket_num)
618625
status = self._read_snsr(socket_num)[0]
619-
if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT):
626+
if status in (
627+
SNSR_SOCK_CLOSED,
628+
SNSR_SOCK_TIME_WAIT,
629+
SNSR_SOCK_FIN_WAIT,
630+
SNSR_SOCK_CLOSE_WAIT,
631+
SNSR_SOCK_CLOSING,
632+
):
620633
if self._debug:
621634
print("* Opening W5k Socket, protocol={}".format(conn_mode))
622635
time.sleep(0.00025)
@@ -714,7 +727,7 @@ def read_udp(self, socket_num, length):
714727
return ret, resp
715728
return -1
716729

717-
def socket_write(self, socket_num, buffer):
730+
def socket_write(self, socket_num, buffer, timeout=0):
718731
"""Writes a bytearray to a provided socket."""
719732
assert self.link_status, "Ethernet cable disconnected!"
720733
assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets."
@@ -725,13 +738,16 @@ def socket_write(self, socket_num, buffer):
725738
ret = SOCK_SIZE
726739
else:
727740
ret = len(buffer)
741+
stamp = time.monotonic()
728742

729743
# if buffer is available, start the transfer
730744
free_size = self._get_tx_free_size(socket_num)
731745
while free_size < ret:
732746
free_size = self._get_tx_free_size(socket_num)
733747
status = self.socket_status(socket_num)[0]
734-
if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT):
748+
if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT) or (
749+
timeout and time.monotonic() - stamp > timeout
750+
):
735751
ret = 0
736752
break
737753

@@ -756,9 +772,11 @@ def socket_write(self, socket_num, buffer):
756772
) != SNIR_SEND_OK:
757773
if self.socket_status(socket_num)[0] in (
758774
SNSR_SOCK_CLOSED,
775+
SNSR_SOCK_TIME_WAIT,
759776
SNSR_SOCK_FIN_WAIT,
760777
SNSR_SOCK_CLOSE_WAIT,
761-
):
778+
SNSR_SOCK_CLOSING,
779+
) or (timeout and time.monotonic() - stamp > timeout):
762780
# self.socket_close(socket_num)
763781
return 0
764782
time.sleep(0.01)

adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class DHCP:
101101

102102
# pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name
103103
def __init__(
104-
self, eth, mac_address, hostname=None, response_timeout=5, debug=False
104+
self, eth, mac_address, hostname=None, response_timeout=30, debug=False
105105
):
106106
self._debug = debug
107107
self._response_timeout = response_timeout

adafruit_wiznet5k/adafruit_wiznet5k_socket.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def htons(x):
6363
TCP_MODE = 80
6464
SOCK_DGRAM = const(0x02) # UDP
6565
AF_INET = const(3)
66-
NO_SOCKET_AVAIL = const(255)
66+
SOCKET_INVALID = const(255)
6767

6868

6969
# pylint: disable=too-many-arguments, unused-argument
@@ -123,6 +123,8 @@ def __init__(
123123
self._listen_port = None
124124

125125
self._socknum = _the_interface.get_socket()
126+
if self._socknum == SOCKET_INVALID:
127+
raise RuntimeError("Failed to allocate socket.")
126128

127129
@property
128130
def socknum(self):
@@ -201,7 +203,7 @@ def send(self, data):
201203
:param bytearray data: Desired data to send to the socket.
202204
203205
"""
204-
_the_interface.socket_write(self.socknum, data)
206+
_the_interface.socket_write(self.socknum, data, self._timeout)
205207
gc.collect()
206208

207209
def recv(self, bufsize=0): # pylint: disable=too-many-branches

adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ def set_interface(iface):
4343
socket.set_interface(iface)
4444

4545

46-
# Maximum number of sockets for the web server, leave one for other things
47-
MAX_SOCK_NUM = const(7)
46+
# Maximum number of sockets for the web server (number of connections we can hold)
47+
MAX_SOCK_NUM = const(6)
4848

4949
# pylint: disable=invalid-name
5050
class WSGIServer:
@@ -55,6 +55,7 @@ class WSGIServer:
5555
def __init__(self, port=80, debug=False, application=None):
5656
self.application = application
5757
self.port = port
58+
self._timeout = 20
5859
self._client_sock = []
5960
self._debug = debug
6061

@@ -69,6 +70,7 @@ def start(self):
6970
"""
7071
for _ in range(MAX_SOCK_NUM):
7172
new_sock = socket.socket()
73+
new_sock.settimeout(self._timeout)
7274
new_sock.bind((None, self.port))
7375
new_sock.listen()
7476
self._client_sock.append(new_sock)
@@ -90,6 +92,7 @@ def update_poll(self):
9092
self.finish_response(result, sock)
9193
self._client_sock.remove(sock)
9294
new_sock = socket.socket()
95+
new_sock.settimeout(self._timeout)
9396
new_sock.bind((None, self.port))
9497
new_sock.listen()
9598
add_sock.append(new_sock)

0 commit comments

Comments
 (0)