ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
async_tcp_socket.cpp
Go to the documentation of this file.
1#include "async_tcp_socket.h"
2
3#if !defined(USE_ESP32) && !defined(USE_ESP8266) && !defined(USE_RP2) && !defined(USE_LIBRETINY) && \
4 (defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS))
5
7#include "esphome/core/log.h"
8#include <cerrno>
9
11
12ESPHOME_LOG_TAG(TAG, "async_tcp");
13
14// Read buffer size matches TCP MSS (1500 MTU - 40 bytes IP/TCP headers).
15// This implementation only runs on ESP-IDF and host which have ample stack.
16static constexpr size_t READ_BUFFER_SIZE = 1460;
17
18bool AsyncClient::connect(const char *host, uint16_t port) {
19 if (connected_ || connecting_) {
20 ESP_LOGW(TAG, "Already connected/connecting");
21 return false;
22 }
23
24 // Resolve address
25 struct sockaddr_storage addr;
26 socklen_t addrlen = esphome::socket::set_sockaddr((struct sockaddr *) &addr, sizeof(addr), host, port);
27 if (addrlen == 0) {
28 ESP_LOGE(TAG, "Invalid address: %s", host);
29 if (error_cb_)
30 error_cb_(error_arg_, this, -1);
31 return false;
32 }
33
34 // Create socket with loop monitoring
35 int family = ((struct sockaddr *) &addr)->sa_family;
36 socket_ = esphome::socket::socket_loop_monitored(family, SOCK_STREAM, IPPROTO_TCP);
37 if (!socket_) {
38 ESP_LOGE(TAG, "Failed to create socket");
39 if (error_cb_)
40 error_cb_(error_arg_, this, -1);
41 return false;
42 }
43
44 if (socket_->setblocking(false) != 0) {
45 // Capture before the log and close() clobber errno
46 const int saved_errno = errno;
47 ESP_LOGE(TAG, "Failed to set nonblocking: errno %d", saved_errno);
48 close();
49 if (error_cb_)
50 error_cb_(error_arg_, this, saved_errno);
51 return false;
52 }
53
54 int err = socket_->connect((struct sockaddr *) &addr, addrlen);
55 if (err == 0) {
56 // Connection succeeded immediately (rare, but possible for localhost)
57 connected_ = true;
58 if (connect_cb_)
59 connect_cb_(connect_arg_, this);
60 return true;
61 }
62 const int saved_errno = errno;
63 if (saved_errno != EINPROGRESS) {
64 ESP_LOGE(TAG, "Connect failed: %d", saved_errno);
65 close();
66 if (error_cb_)
67 error_cb_(error_arg_, this, saved_errno);
68 return false;
69 }
70
71 connecting_ = true;
72 return true;
73}
74
76 socket_.reset();
77 bool was_connected = connected_;
78 connected_ = false;
79 connecting_ = false;
80 if (was_connected && disconnect_cb_)
81 disconnect_cb_(disconnect_arg_, this);
82}
83
84size_t AsyncClient::write(const char *data, size_t len) {
85 if (!socket_ || !connected_)
86 return 0;
87
88 ssize_t sent = socket_->write(data, len);
89 if (sent < 0) {
90 const int err = errno;
91 if (err != EAGAIN && err != EWOULDBLOCK) {
92 ESP_LOGE(TAG, "Write error: %d", err);
93 close();
94 if (error_cb_)
95 error_cb_(error_arg_, this, err);
96 }
97 return 0;
98 }
99 return sent;
100}
101
103 if (!socket_)
104 return;
105
106 if (connecting_) {
107 int err = 0;
108 switch (socket::poll_connect(*socket_, err)) {
110 break;
112 connecting_ = false;
113 connected_ = true;
114 if (connect_cb_)
115 connect_cb_(connect_arg_, this);
116 break;
118 ESP_LOGW(TAG, "Connection failed: %d", err);
119 close();
120 if (error_cb_)
121 error_cb_(error_arg_, this, err);
122 break;
123 }
124 } else if (connected_) {
125 // For connected sockets, use the Application's select() results
126 if (!socket_->ready())
127 return;
128
129 uint8_t buf[READ_BUFFER_SIZE];
130 ssize_t len = socket_->read(buf, READ_BUFFER_SIZE);
131
132 if (len == 0) {
133 ESP_LOGI(TAG, "Connection closed by peer");
134 close();
135 } else if (len > 0) {
136 if (data_cb_)
137 data_cb_(data_arg_, this, buf, len);
138 } else {
139 const int err = errno;
140 if (err != EAGAIN && err != EWOULDBLOCK) {
141 ESP_LOGW(TAG, "Read error: %d", err);
142 close();
143 if (error_cb_)
144 error_cb_(error_arg_, this, err);
145 }
146 }
147 }
148}
149
150} // namespace esphome::async_tcp
151
152#endif
bool connect(const char *host, uint16_t port)
size_t write(const char *data, size_t len)
uint32_t socklen_t
Definition headers.h:109
__int64 ssize_t
Definition httplib.h:178
ESPHOME_LOG_TAG(TAG, "async_tcp")
ConnectPollResult poll_connect(Socket &sock, int &err_out)
Poll a connect() that returned EINPROGRESS.
Definition socket.h:223
socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const char *ip_address, uint16_t port)
Set a sockaddr to the specified address and port for the IP version used by socket_ip().
std::unique_ptr< Socket > socket_loop_monitored(int domain, int type, int protocol)
Create a socket and monitor it for data in the main loop.
const void size_t len
Definition hal.h:68