Closed anecdata closed 1 year ago
Turns out WIZnet TCP Server works if the server sets a timeout for the accepted connection. This is different than in CPython and CircuitPython Espressif native:
The following works in CPYthon (and CircuitPython Espressif native), with blocking on the accepted connection:
Server:
#!/usr/bin/env python3
import socket
HOST = ""
PORT = 5010
TIMEOUT = None
# CONN_TIMEOUT = 5
MAXBUF = 256
print("Create TCP Server Socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
s.bind((HOST, PORT))
s.listen()
print("Listening")
while True:
print("Accepting connections")
conn, addr = s.accept()
conn.settimeout(TIMEOUT) # CONN_TIMEOUT for WIZnet
print("Accepted from", addr)
buf = conn.recv(MAXBUF)
print("Received", buf, "from", addr)
size = conn.sendall(buf)
print("Sent", buf[:size], size, "bytes to", addr)
conn.close()
Client:
#!/usr/bin/env python3
import socket
import time
# edit host and port to match server
HOST = "localhost"
PORT = 5010
TIMEOUT = 10
INTERVAL = 5
MAXBUF = 256
while True:
print("Create TCP Client Socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
print(f"Connecting to {HOST}, {PORT}")
s.connect((HOST, PORT))
size = s.send(b'Hello, world')
print("Sent", size, "bytes")
buf = s.recv(MAXBUF)
print('Received', buf)
s.close()
time.sleep(INTERVAL)
I think what may be happening is that in the other two contexts, the server recognizes that the client has closed its end of the connection and the server stops reading, but the WIZnet socket does not.
Another quirk:
CPython:
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>>
>>> _ = s.gettimeout()
>>> _, repr(_), type(_)
(None, 'None', <class 'NoneType'>)
>>> # CPython default: blocking; ∞ timeout
>>>
>>> s.settimeout(None)
>>>_, repr(_), type(_)
(None, 'None', <class 'NoneType'>)
WIZnet5k library:
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>>
>>> _ = s.gettimeout()
>>> _, repr(_), type(_)
(0, '0', <class 'int'>)
>>> # WIZnet default: non-blocking; 0 timeout
>>>
>>> s.settimeout(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "adafruit_wiznet5k/adafruit_wiznet5k_socket.py", line 413, in settimeout
TypeError: unsupported types for __lt__: 'NoneType', 'int'
>>> # can't set sockets to blocking
Addendum: as of 2.0.0, this is now consistent with CPython:
>>> _ = s.gettimeout()
>>> _, repr(_), type(_)
(None, 'None', <class 'NoneType'>)
>>> s.settimeout(None)
>>>
With recent changes to the library, WIZnet server now works.
WIZnet Server test code:
import board
import busio
import digitalio
import time
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket
# WIZnet Ethernet Hat on Raspberry Pi Pico [W]
cs = digitalio.DigitalInOut(board.GP17)
spi = busio.SPI(board.GP18, MOSI=board.GP19, MISO=board.GP16)
eth = WIZNET5K(spi, cs, mac=(0xDE, 0xAD, 0xBE, 0xEF, 0x06, 247), is_dhcp=True)
HOST = eth.pretty_ip(eth.ip_address)
PORT = 5000
TIMEOUT = None
MAXBUF = 256
print(f"WIZnet Client IP Address: {HOST}")
print("Create TCP Server Socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
s.bind((HOST, PORT))
s.listen()
print(f"Listening on {HOST}, {PORT}")
buf = bytearray(MAXBUF)
while True:
print("Accepting connections")
conn, addr = s.accept()
conn.settimeout(TIMEOUT)
print("Accepted from", addr)
size = conn.recv_into(buf, MAXBUF)
print("Received", buf[:size], size, "bytes")
conn.send(buf[:size])
print("Sent", buf[:size], size, "bytes")
conn.close()
CPython client test code:
#!/usr/bin/env python3
import socket
import time
# edit host and port to match server
HOST = "192.168.6.247"
PORT = 5000
TIMEOUT = 60
INTERVAL = 5
MAXBUF = 256
while True:
print("Create TCP Client Socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
print("Connecting")
s.connect((HOST, PORT))
size = s.send(b'Hello, world')
print("Sent", size, "bytes")
buf = s.recv(MAXBUF)
print('Received', buf)
s.close()
time.sleep(INTERVAL)
This presumably used to work, or works with some variation that's not apparent to me, as there are examples in the repo.
TCP and UDP clients are fine with both WIZnet devices, and are interoperable with CPython.
I've tried a number of variations (including the example in this repo, with only the necessary locale changes), but the client and server structure below works for CPython and ESP32-S2, but not on RP2040+Ethernet FeatherWing:
Adafruit CircuitPython 7.2.0-alpha.1-224-gac7a80753 on 2022-01-26; Adafruit Feather RP2040 with rp2040
or on WIZnet W5100S-EVB-Pico:Adafruit CircuitPython 7.2.0-alpha.1-224-gac7a80753 on 2022-01-26; Raspberry Pi Pico with rp2040
Info below refers to a configuration with a CPython TCP client, and the TCP Server on Adafruit Feather RP2040 with an Adafruit Ethernet FeatherWing...
Client code:
Server code:
Client output:
Server output:
Server Debug output:
Client Exception if control-C during receive:
Server Exception if control-C after
accept
(before client times out):Server Exception if control-C after
accept
and after client times out:(note different line number in
_get_rx_rcv_size
)