
From the model to your ESP32
On an ESP32, WiFiClient is TCP and WiFiUDP is UDP, and both sit on top of lwIP, IP and Wi-Fi. A successful connect proves only the TCP handshake; each layer can fail in its own way, and the way it fails tells you which one.
In the Arduino core for the ESP32, WiFiClient is TCP and WiFiUDP is UDP. Both are thin names over the BSD sockets in lwIP, the small IP stack the chip runs, and lwIP sits on the Wi-Fi driver. So a call you write passes down through four layers: your application, the transport, IP, Wi-Fi. Each has its own way to fail.
What a call proves
| Call | What it proves | What it does not |
|---|---|---|
client.connect(host, port) returns true | The TCP handshake finished | That the program answers, or that HTTP, MQTT or TLS above it work |
udp.endPacket() returns 1 | lwIP accepted the datagram | That it left the radio, arrived, or was read |
connect calls lwIP's connect, waits until the socket is writable, and checks the socket's error. It knows nothing about the protocol on top. endPacket returns 1 after a successful send into lwIP and 0 on error. UDP has no handshake, so nothing waits for anyone. HTTP from a board and MQTT and a broker both start from a connect that proved only this.
Which layer complains
Break each layer and you get a different answer. With the Wi-Fi gone, the Wi-Fi layer is the one that knows, and the layers above it only wait. A firewall that drops the SYN gives silence: connect retries and gives up. A host with no program on that port answers with a RST, as in lesson 10, and connect fails at once, which is the clearest answer you can get. A program that is listening but stuck gets the handshake completed for it by the TCP stack, until its backlog fills, so connect returns true and nothing comes back. Only the application layer can say it is broken.
When the Wi-Fi vanishes
A TCP connection that is idle never learns the other side is gone. Keep-alive is off by default, the standard requires it, and the Arduino core's connect has the call that would turn it on commented out. A socket with unsent data finds out only after its retransmissions run out, and the honest answer is that it can be a long time. An idle one may never notice. Send something of your own now and then, as the WebSocket page advises.
UDP replies depend on the router. The standard asks a NAT to keep a UDP mapping for at least two minutes. Do not rely on it, and send something at least every fifteen seconds if you need the path open.
Common mistakes
- Treating a true
connectas "the server is ready". It is the handshake and nothing else. - Treating
endPacket()returning 1 as delivery. It means the datagram reached lwIP. - Waiting for TCP to notice a dead Wi-Fi. Use a heartbeat of your own, or turn keep-alive on.
- Sending a long UDP datagram and expecting it all.
parsePacketin this core reads up to 1460 bytes, and anything longer is cut.
For the names and the clock, see names and time and keeping time; for joining a network, station mode.
Edit this page — content/fundamentals/tcp-udp/on-your-esp32.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.