Align connection timing with other cores#21
Align connection timing with other cores#21MitchBradley wants to merge 5 commits intopschatzmann:mainfrom
Conversation
* connected() no longer waits * connect() includes a timeout * The timeout applies to DNS resolution and connection combined * There are extra connect( ..., timeout_ms) forms as with ESP
There was a problem hiding this comment.
Pull request overview
This PR adjusts Arduino-Emulator’s TCP client connection semantics to better match ESP32 and Arduino-Pico behavior, particularly around fast connected() probing and timeout-bounded connect().
Changes:
- Adds
connect(host, port, timeout_ms)overloads and routes defaultconnect()through the configured connection timeout. - Makes
connected()fail fast on disconnect and treatsrecv(..., MSG_PEEK) == 0as peer close, clearing socket state on close. - Improves connection failure propagation (don’t report success when socket connect fails) and tightens socket-state handling in
available()/read().
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ArduinoCore-Linux/cores/arduino/SocketImpl.h | Adds a timeout-aware connect() overload to the socket abstraction. |
| ArduinoCore-Linux/cores/arduino/SocketImpl.cpp | Implements fast-probe connected(), timeout-bounded connect via nonblocking+select, and clears socket state on close. |
| ArduinoCore-Linux/cores/arduino/Ethernet.h | Updates EthernetClient to use fast connected(), adds timeout-aware connect overloads, and switches hostname resolution to getaddrinfo. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (sock < 0) { | ||
| is_connected = false; | ||
| return static_cast<size_t>(-1); | ||
| } | ||
|
|
||
| ssize_t result = ::recv(sock, buffer, len, MSG_DONTWAIT | MSG_NOSIGNAL); | ||
| if (result == 0) { | ||
| Logger.info(SOCKET_IMPL, "read EOF"); | ||
| close(); | ||
| is_connected = false; | ||
| return static_cast<size_t>(-1); | ||
| } |
There was a problem hiding this comment.
SocketImpl::read() returns static_cast<size_t>(-1) for EOF and for non-fatal nonblocking cases (EAGAIN/EWOULDBLOCK). Because the return type is size_t, this becomes a very large positive value, and call sites that store it in an int (e.g. EthernetClient::read) can misinterpret errors as successful reads. Prefer returning 0 when no bytes are available/EOF (and updating is_connected), or change the API to return a signed type (ssize_t/int) consistently through the stack.
|
How did you test these changes ? |
|
I did some review and think we should change the method signature of SocketImpl virtual size_t read(uint8_t* buffer, size_t len); to return ssize_t or int. I think returning -1 for size_t is a really bad idea. You changed the the logic of a valid 0 byte return to return -1. I think this should not be treated as an error. Then we can improve the logic in Ethernet int read(uint8_t* buffer, size_t len) to abort the timeout loop if there was an error! |
Co-authored-by: Copilot <[email protected]>
I had been testing with FluidNC but that was not comprehensive in that it did not exercise all of the timing possibilities. I created and pushed a new test examples/network-connect-timing to exercise the cases, in conjunction with a rudimentary "nc" server on localhost, plus packet filter dropping rules to simulate network delays that would result in a timeout as opposed to an instant reject. The header commentary in network-connect-timing explains how to run it and interpret the results. I will now look at the ssize thing. |
I think int is the right answer. ssize_t is not aligned with other aspects of Arduino class conventions, and ends up having to be narrowed elsewhere. It is clearer/cleaner to do the narrowing at the lowest level instead of propagating it up to several other places. int rules in Arduino Land. GPT-5.4 helped me find all of the fallout from the change. 4850094 purports to implement this effort. |
Summary
Align
WiFiClient/EthernetClient/SocketImplconnection behavior with ESP32 and Arduino-Pico client semantics.Why
Cross-platform Arduino code expects two things that were not true in Arduino-Emulator:
connected()should be a fast status probe, not a multi-second wait before returningfalseconnect()should be timeout-bounded in a way that matches mainstream Arduino network client behaviorChanges
connected()fail fast on disconnectrecv(..., MSG_PEEK) == 0as peer closeconnect(..., timeout_ms)overloads as with the ESP coreconnect()use the configured connection timeout as with the Pico coreCompatibility
This brings Arduino-Emulator closer to other cores: