-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ESP LWIP network stack cannot handle 3 binds correctly. #8363
Comments
Had to tweak the server code.pyimport wifi
from socketpool import SocketPool
pool = SocketPool(wifi.radio)
_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket2 = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket.bind(("0.0.0.0", 20))
_socket.listen(1)
_socket2.bind(("0.0.0.0", 21))
_socket2.listen(1)
a = _socket.accept()
b = _socket2.accept()
a[0].close()
b[0].close()
print("ok") |
works in server code.pyimport asyncio
import wifi
import socketpool
PORT1 = 20
PORT2 = 21
async def tcpserver(PORT):
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.bind(("", PORT))
s.listen(1)
s.settimeout(0)
while True:
try:
conn, addr = s.accept()
print(f"{PORT} OK {addr}")
conn.close()
except OSError: # EAGAIN
pass
await asyncio.sleep(0)
pool = socketpool.SocketPool(wifi.radio)
async def main():
t1 = asyncio.create_task(tcpserver(PORT1))
t2 = asyncio.create_task(tcpserver(PORT2))
await asyncio.gather(t1, t2)
asyncio.run(main()) CPython client code for both cases above#!/usr/bin/env python3
import socket
import time
import random
# edit host and port to match server
HOST = "192.168.6.198"
PORTS = (20, 21)
TIMEOUT = 5
INTERVAL = 1
while True:
PORT = random.choice(PORTS) # just for fun
print("Create TCP Client Socket")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(TIMEOUT)
print("Connecting")
s.connect((HOST, PORT))
size = s.send(b'Hello, world')
print(f"Sent {size} bytes to {HOST}:{PORT}")
time.sleep(INTERVAL) edit: I guess b/c only one is connected at a time, may be a useful workaround |
Regarding asyncio, I drafted this: import asyncio
import wifi
import socketpool
from sys import stdout
conn1 = None
conn2 = None
async def tcpserver1():
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.bind(("0.0.0.0", 20))
s.listen(1)
s.settimeout(10)
conn1, addr = s.accept()
print(f"{conn1} OK {addr}")
async def tcpserver2():
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.bind(("0.0.0.0", 21))
s.listen(1)
s.settimeout(10)
conn2, addr = s.accept()
print(f"{conn2} OK {addr}")
async def tcpserver1close():
conn1.close()
print("Closed 1")
async def tcpserver2close():
conn2.close()
print("Closed 2")
pool = socketpool.SocketPool(wifi.radio)
async def main():
t1 = asyncio.create_task(tcpserver1())
t2 = asyncio.create_task(tcpserver2())
t3 = asyncio.create_task(tcpserver1close())
t4 = asyncio.create_task(tcpserver2close())
await asyncio.gather(t1, t2)
await asyncio.gather(t3, t4)
asyncio.run(main()) Which as you see, also doesn't work. Something to note: import wifi
from socketpool import SocketPool
from adafruit_requests import Session
pool = SocketPool(wifi.radio)
_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket2 = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket.bind(("0.0.0.0", 20))
_socket.listen(1)
_socket2.bind(("0.0.0.0", 20))
_socket2.listen(1)
print("Accepting 1")
a = _socket.accept()
print("Accepting 2")
b = _socket2.accept()
print("Accepted 2")
_socket.close()
print("ok") Is also a nono. import wifi
from socketpool import SocketPool
from adafruit_requests import Session
pool = SocketPool(wifi.radio)
_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket2 = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket.bind(("0.0.0.0", 20))
_socket.listen(2)
print("Accepting 1")
a = _socket.accept()
print("Accepting 2")
b = _socket.accept()
print("Accepted 2")
_socket.close()
print("ok") Works just fine..
And dolphin w/PASV doesn't work with it, even if the cli does.
Passing everythin over the control port isn't something ftp is designed for it so seems. |
There does seem to be an I fully expect sequential (close before next connect) connections (to the same or different ports) to work (your 3rd example, and my But PASV doesn't work with sequential connection to the control port, then the data port? |
I think it would be good to test this with, say, MicroPython on some ESP32xx board to see if it is our issue or is ESP-IDF. Also maybe a quick search of the ESP-iDF issues. |
PASV tl;dr workflow explanation:
This whole time, the control connection remains open and in use. |
New discovery! If the web workflow is disabled, you can do 2 binds. import wifi
from socketpool import SocketPool
from sys import exit
try:
wifi.radio.connect("Thinkpood", "REDACTED")
except:
pass
if not wifi.radio.connected:
print("No wifi")
exit(0)
pool = SocketPool(wifi.radio)
_socket = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket.bind(("", 20))
_socket.listen(1)
_socket2 = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket2.bind(("", 21))
_socket2.listen(1)
print("Accepting 1")
a = _socket.accept()
print("Accepting 2")
b = _socket2.accept()
print("Accepted 2")
_socket.close()
_socket2.close()
print("ok") |
Taking the same code and adding: _socket3 = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
_socket3.bind(("", 22)) in it, is all that is required to trigger the bug. This gives me some good clues as to where to look. |
I did plently of esp debugging. I will have to leave it you you guys from there on out. |
I went and put micropython 1.20 on one of my s2 boards, and this is not reproducible. |
This issue is fully resolved. |
CircuitPython version
Code/REPL
Behavior
If we were to just telnet into both, the program would print "ok" and close both connections.
However, we can only connect on the first.
The second is stuck at SYN_WAIT.
Description
No response
Additional information
This is needed to properly implement PASV for ftp.
ACTIVE with a bind & a connection, works just fine.
Reproducible on S2 too.
The text was updated successfully, but these errors were encountered: