ESPHome 2025.12.0-dev
Loading...
Searching...
No Matches
lwip_raw_tcp_impl.cpp
Go to the documentation of this file.
1#include "socket.h"
3
4#ifdef USE_SOCKET_IMPL_LWIP_TCP
5
6#include "lwip/ip.h"
7#include "lwip/netif.h"
8#include "lwip/opt.h"
9#include "lwip/tcp.h"
10#include <cerrno>
11#include <cstring>
12#include <array>
13
15#include "esphome/core/log.h"
16
17namespace esphome {
18namespace socket {
19
20static const char *const TAG = "socket.lwip";
21
22// set to 1 to enable verbose lwip logging
23#if 0
24#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
25#else
26#define LWIP_LOG(msg, ...)
27#endif
28
29class LWIPRawImpl : public Socket {
30 public:
31 LWIPRawImpl(sa_family_t family, struct tcp_pcb *pcb) : pcb_(pcb), family_(family) {}
32 ~LWIPRawImpl() override {
33 if (pcb_ != nullptr) {
34 LWIP_LOG("tcp_abort(%p)", pcb_);
35 tcp_abort(pcb_);
36 pcb_ = nullptr;
37 }
38 }
39
40 void init() {
41 LWIP_LOG("init(%p)", pcb_);
42 tcp_arg(pcb_, this);
43 tcp_recv(pcb_, LWIPRawImpl::s_recv_fn);
44 tcp_err(pcb_, LWIPRawImpl::s_err_fn);
45 }
46
47 std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) override {
48 // Non-listening sockets return error
49 errno = EINVAL;
50 return nullptr;
51 }
52 int bind(const struct sockaddr *name, socklen_t addrlen) override {
53 if (pcb_ == nullptr) {
54 errno = EBADF;
55 return -1;
56 }
57 if (name == nullptr) {
58 errno = EINVAL;
59 return 0;
60 }
61 ip_addr_t ip;
62 in_port_t port;
63#if LWIP_IPV6
64 if (family_ == AF_INET) {
65 if (addrlen < sizeof(sockaddr_in)) {
66 errno = EINVAL;
67 return -1;
68 }
69 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
70 port = ntohs(addr4->sin_port);
71 ip.type = IPADDR_TYPE_V4;
72 ip.u_addr.ip4.addr = addr4->sin_addr.s_addr;
73 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", pcb_, ip4addr_ntoa(&ip.u_addr.ip4), port);
74 } else if (family_ == AF_INET6) {
75 if (addrlen < sizeof(sockaddr_in6)) {
76 errno = EINVAL;
77 return -1;
78 }
79 auto *addr6 = reinterpret_cast<const sockaddr_in6 *>(name);
80 port = ntohs(addr6->sin6_port);
81 ip.type = IPADDR_TYPE_ANY;
82 memcpy(&ip.u_addr.ip6.addr, &addr6->sin6_addr.un.u8_addr, 16);
83 LWIP_LOG("tcp_bind(%p ip=%s port=%u)", pcb_, ip6addr_ntoa(&ip.u_addr.ip6), port);
84 } else {
85 errno = EINVAL;
86 return -1;
87 }
88#else
89 if (family_ != AF_INET) {
90 errno = EINVAL;
91 return -1;
92 }
93 auto *addr4 = reinterpret_cast<const sockaddr_in *>(name);
94 port = ntohs(addr4->sin_port);
95 ip.addr = addr4->sin_addr.s_addr;
96 LWIP_LOG("tcp_bind(%p ip=%u port=%u)", pcb_, ip.addr, port);
97#endif
98 err_t err = tcp_bind(pcb_, &ip, port);
99 if (err == ERR_USE) {
100 LWIP_LOG(" -> err ERR_USE");
101 errno = EADDRINUSE;
102 return -1;
103 }
104 if (err == ERR_VAL) {
105 LWIP_LOG(" -> err ERR_VAL");
106 errno = EINVAL;
107 return -1;
108 }
109 if (err != ERR_OK) {
110 LWIP_LOG(" -> err %d", err);
111 errno = EIO;
112 return -1;
113 }
114 return 0;
115 }
116 int close() override {
117 if (pcb_ == nullptr) {
118 errno = ECONNRESET;
119 return -1;
120 }
121 LWIP_LOG("tcp_close(%p)", pcb_);
122 err_t err = tcp_close(pcb_);
123 if (err != ERR_OK) {
124 LWIP_LOG(" -> err %d", err);
125 tcp_abort(pcb_);
126 pcb_ = nullptr;
127 errno = err == ERR_MEM ? ENOMEM : EIO;
128 return -1;
129 }
130 pcb_ = nullptr;
131 return 0;
132 }
133 int shutdown(int how) override {
134 if (pcb_ == nullptr) {
135 errno = ECONNRESET;
136 return -1;
137 }
138 bool shut_rx = false, shut_tx = false;
139 if (how == SHUT_RD) {
140 shut_rx = true;
141 } else if (how == SHUT_WR) {
142 shut_tx = true;
143 } else if (how == SHUT_RDWR) {
144 shut_rx = shut_tx = true;
145 } else {
146 errno = EINVAL;
147 return -1;
148 }
149 LWIP_LOG("tcp_shutdown(%p shut_rx=%d shut_tx=%d)", pcb_, shut_rx ? 1 : 0, shut_tx ? 1 : 0);
150 err_t err = tcp_shutdown(pcb_, shut_rx, shut_tx);
151 if (err != ERR_OK) {
152 LWIP_LOG(" -> err %d", err);
153 errno = err == ERR_MEM ? ENOMEM : EIO;
154 return -1;
155 }
156 return 0;
157 }
158
159 int getpeername(struct sockaddr *name, socklen_t *addrlen) override {
160 if (pcb_ == nullptr) {
161 errno = ECONNRESET;
162 return -1;
163 }
164 if (name == nullptr || addrlen == nullptr) {
165 errno = EINVAL;
166 return -1;
167 }
168 return this->ip2sockaddr_(&pcb_->local_ip, pcb_->local_port, name, addrlen);
169 }
170 std::string getpeername() override {
171 if (pcb_ == nullptr) {
172 errno = ECONNRESET;
173 return "";
174 }
175 return this->format_ip_address_(pcb_->remote_ip);
176 }
177 int getsockname(struct sockaddr *name, socklen_t *addrlen) override {
178 if (pcb_ == nullptr) {
179 errno = ECONNRESET;
180 return -1;
181 }
182 if (name == nullptr || addrlen == nullptr) {
183 errno = EINVAL;
184 return -1;
185 }
186 return this->ip2sockaddr_(&pcb_->local_ip, pcb_->local_port, name, addrlen);
187 }
188 std::string getsockname() override {
189 if (pcb_ == nullptr) {
190 errno = ECONNRESET;
191 return "";
192 }
193 return this->format_ip_address_(pcb_->local_ip);
194 }
195 int getsockopt(int level, int optname, void *optval, socklen_t *optlen) override {
196 if (pcb_ == nullptr) {
197 errno = ECONNRESET;
198 return -1;
199 }
200 if (optlen == nullptr || optval == nullptr) {
201 errno = EINVAL;
202 return -1;
203 }
204 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
205 if (*optlen < 4) {
206 errno = EINVAL;
207 return -1;
208 }
209
210 // lwip doesn't seem to have this feature. Don't send an error
211 // to prevent warnings
212 *reinterpret_cast<int *>(optval) = 1;
213 *optlen = 4;
214 return 0;
215 }
216 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
217 if (*optlen < 4) {
218 errno = EINVAL;
219 return -1;
220 }
221 *reinterpret_cast<int *>(optval) = nodelay_;
222 *optlen = 4;
223 return 0;
224 }
225
226 errno = EINVAL;
227 return -1;
228 }
229 int setsockopt(int level, int optname, const void *optval, socklen_t optlen) override {
230 if (pcb_ == nullptr) {
231 errno = ECONNRESET;
232 return -1;
233 }
234 if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
235 if (optlen != 4) {
236 errno = EINVAL;
237 return -1;
238 }
239
240 // lwip doesn't seem to have this feature. Don't send an error
241 // to prevent warnings
242 return 0;
243 }
244 if (level == IPPROTO_TCP && optname == TCP_NODELAY) {
245 if (optlen != 4) {
246 errno = EINVAL;
247 return -1;
248 }
249 int val = *reinterpret_cast<const int *>(optval);
250 nodelay_ = val;
251 return 0;
252 }
253
254 errno = EINVAL;
255 return -1;
256 }
257 int listen(int backlog) override {
258 // Regular sockets can't be converted to listening - this shouldn't happen
259 // as listen() should only be called on sockets created for listening
260 errno = EOPNOTSUPP;
261 return -1;
262 }
263 ssize_t read(void *buf, size_t len) override {
264 if (pcb_ == nullptr) {
265 errno = ECONNRESET;
266 return -1;
267 }
268 if (rx_closed_ && rx_buf_ == nullptr) {
269 return 0;
270 }
271 if (len == 0) {
272 return 0;
273 }
274 if (rx_buf_ == nullptr) {
275 errno = EWOULDBLOCK;
276 return -1;
277 }
278
279 size_t read = 0;
280 uint8_t *buf8 = reinterpret_cast<uint8_t *>(buf);
281 while (len && rx_buf_ != nullptr) {
282 size_t pb_len = rx_buf_->len;
283 size_t pb_left = pb_len - rx_buf_offset_;
284 if (pb_left == 0)
285 break;
286 size_t copysize = std::min(len, pb_left);
287 memcpy(buf8, reinterpret_cast<uint8_t *>(rx_buf_->payload) + rx_buf_offset_, copysize);
288
289 if (pb_left == copysize) {
290 // full pb copied, free it
291 if (rx_buf_->next == nullptr) {
292 // last buffer in chain
293 pbuf_free(rx_buf_);
294 rx_buf_ = nullptr;
295 rx_buf_offset_ = 0;
296 } else {
297 auto *old_buf = rx_buf_;
298 rx_buf_ = rx_buf_->next;
299 pbuf_ref(rx_buf_);
300 pbuf_free(old_buf);
301 rx_buf_offset_ = 0;
302 }
303 } else {
304 rx_buf_offset_ += copysize;
305 }
306 LWIP_LOG("tcp_recved(%p %u)", pcb_, copysize);
307 tcp_recved(pcb_, copysize);
308
309 buf8 += copysize;
310 len -= copysize;
311 read += copysize;
312 }
313
314 if (read == 0) {
315 errno = EWOULDBLOCK;
316 return -1;
317 }
318
319 return read;
320 }
321 ssize_t readv(const struct iovec *iov, int iovcnt) override {
322 ssize_t ret = 0;
323 for (int i = 0; i < iovcnt; i++) {
324 ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
325 if (err == -1) {
326 if (ret != 0)
327 // if we already read some don't return an error
328 break;
329 return err;
330 }
331 ret += err;
332 if ((size_t) err != iov[i].iov_len)
333 break;
334 }
335 return ret;
336 }
337 ssize_t internal_write(const void *buf, size_t len) {
338 if (pcb_ == nullptr) {
339 errno = ECONNRESET;
340 return -1;
341 }
342 if (len == 0)
343 return 0;
344 if (buf == nullptr) {
345 errno = EINVAL;
346 return 0;
347 }
348 auto space = tcp_sndbuf(pcb_);
349 if (space == 0) {
350 errno = EWOULDBLOCK;
351 return -1;
352 }
353 size_t to_send = std::min((size_t) space, len);
354 LWIP_LOG("tcp_write(%p buf=%p %u)", pcb_, buf, to_send);
355 err_t err = tcp_write(pcb_, buf, to_send, TCP_WRITE_FLAG_COPY);
356 if (err == ERR_MEM) {
357 LWIP_LOG(" -> err ERR_MEM");
358 errno = EWOULDBLOCK;
359 return -1;
360 }
361 if (err != ERR_OK) {
362 LWIP_LOG(" -> err %d", err);
363 errno = ECONNRESET;
364 return -1;
365 }
366 return to_send;
367 }
368 int internal_output() {
369 LWIP_LOG("tcp_output(%p)", pcb_);
370 err_t err = tcp_output(pcb_);
371 if (err == ERR_ABRT) {
372 LWIP_LOG(" -> err ERR_ABRT");
373 // sometimes lwip returns ERR_ABRT for no apparent reason
374 // the connection works fine afterwards, and back with ESPAsyncTCP we
375 // indirectly also ignored this error
376 // FIXME: figure out where this is returned and what it means in this context
377 return 0;
378 }
379 if (err != ERR_OK) {
380 LWIP_LOG(" -> err %d", err);
381 errno = ECONNRESET;
382 return -1;
383 }
384 return 0;
385 }
386 ssize_t write(const void *buf, size_t len) override {
387 ssize_t written = internal_write(buf, len);
388 if (written == -1)
389 return -1;
390 if (written == 0)
391 // no need to output if nothing written
392 return 0;
393 if (nodelay_) {
394 int err = internal_output();
395 if (err == -1)
396 return -1;
397 }
398 return written;
399 }
400 ssize_t writev(const struct iovec *iov, int iovcnt) override {
401 ssize_t written = 0;
402 for (int i = 0; i < iovcnt; i++) {
403 ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
404 if (err == -1) {
405 if (written != 0)
406 // if we already read some don't return an error
407 break;
408 return err;
409 }
410 written += err;
411 if ((size_t) err != iov[i].iov_len)
412 break;
413 }
414 if (written == 0)
415 // no need to output if nothing written
416 return 0;
417 if (nodelay_) {
418 int err = internal_output();
419 if (err == -1)
420 return -1;
421 }
422 return written;
423 }
424 ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) override {
425 // return ::sendto(fd_, buf, len, flags, to, tolen);
426 errno = ENOSYS;
427 return -1;
428 }
429 int setblocking(bool blocking) override {
430 if (pcb_ == nullptr) {
431 errno = ECONNRESET;
432 return -1;
433 }
434 if (blocking) {
435 // blocking operation not supported
436 errno = EINVAL;
437 return -1;
438 }
439 return 0;
440 }
441
442 void err_fn(err_t err) {
443 LWIP_LOG("err(err=%d)", err);
444 // "If a connection is aborted because of an error, the application is alerted of this event by
445 // the err callback."
446 // pcb is already freed when this callback is called
447 // ERR_RST: connection was reset by remote host
448 // ERR_ABRT: aborted through tcp_abort or TCP timer
449 pcb_ = nullptr;
450 }
451 err_t recv_fn(struct pbuf *pb, err_t err) {
452 LWIP_LOG("recv(pb=%p err=%d)", pb, err);
453 if (err != 0) {
454 // "An error code if there has been an error receiving Only return ERR_ABRT if you have
455 // called tcp_abort from within the callback function!"
456 rx_closed_ = true;
457 return ERR_OK;
458 }
459 if (pb == nullptr) {
460 rx_closed_ = true;
461 return ERR_OK;
462 }
463 if (rx_buf_ == nullptr) {
464 // no need to copy because lwIP gave control of it to us
465 rx_buf_ = pb;
466 rx_buf_offset_ = 0;
467 } else {
468 pbuf_cat(rx_buf_, pb);
469 }
470 return ERR_OK;
471 }
472
473 static void s_err_fn(void *arg, err_t err) {
474 LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
475 arg_this->err_fn(err);
476 }
477
478 static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err) {
479 LWIPRawImpl *arg_this = reinterpret_cast<LWIPRawImpl *>(arg);
480 return arg_this->recv_fn(pb, err);
481 }
482
483 protected:
484 std::string format_ip_address_(const ip_addr_t &ip) {
485 char buffer[50] = {};
486 if (IP_IS_V4_VAL(ip)) {
487 inet_ntoa_r(ip, buffer, sizeof(buffer));
488 }
489#if LWIP_IPV6
490 else if (IP_IS_V6_VAL(ip)) {
491 inet6_ntoa_r(ip, buffer, sizeof(buffer));
492 }
493#endif
494 return std::string(buffer);
495 }
496
497 int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen) {
498 if (family_ == AF_INET) {
499 if (*addrlen < sizeof(struct sockaddr_in)) {
500 errno = EINVAL;
501 return -1;
502 }
503
504 struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(name);
505 addr->sin_family = AF_INET;
506 *addrlen = addr->sin_len = sizeof(struct sockaddr_in);
507 addr->sin_port = port;
508 inet_addr_from_ip4addr(&addr->sin_addr, ip_2_ip4(ip));
509 return 0;
510 }
511#if LWIP_IPV6
512 else if (family_ == AF_INET6) {
513 if (*addrlen < sizeof(struct sockaddr_in6)) {
514 errno = EINVAL;
515 return -1;
516 }
517
518 struct sockaddr_in6 *addr = reinterpret_cast<struct sockaddr_in6 *>(name);
519 addr->sin6_family = AF_INET6;
520 *addrlen = addr->sin6_len = sizeof(struct sockaddr_in6);
521 addr->sin6_port = port;
522
523 // AF_INET6 sockets are bound to IPv4 as well, so we may encounter IPv4 addresses that must be converted to IPv6.
524 if (IP_IS_V4(ip)) {
525 ip_addr_t mapped;
526 ip4_2_ipv4_mapped_ipv6(ip_2_ip6(&mapped), ip_2_ip4(ip));
527 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(&mapped));
528 } else {
529 inet6_addr_from_ip6addr(&addr->sin6_addr, ip_2_ip6(ip));
530 }
531 return 0;
532 }
533#endif
534 return -1;
535 }
536
537 // Member ordering optimized to minimize padding on 32-bit systems
538 // Largest members first (4 bytes), then smaller members (1 byte each)
539 struct tcp_pcb *pcb_;
540 pbuf *rx_buf_ = nullptr;
541 size_t rx_buf_offset_ = 0;
542 bool rx_closed_ = false;
543 // don't use lwip nodelay flag, it sometimes causes reconnect
544 // instead use it for determining whether to call lwip_output
545 bool nodelay_ = false;
546 sa_family_t family_ = 0;
547};
548
549// Listening socket class - only allocates accept queue when needed (for bind+listen sockets)
550// This saves 16 bytes (12 bytes array + 1 byte count + 3 bytes padding) for regular connected sockets on ESP8266/RP2040
551class LWIPRawListenImpl : public LWIPRawImpl {
552 public:
553 LWIPRawListenImpl(sa_family_t family, struct tcp_pcb *pcb) : LWIPRawImpl(family, pcb) {}
554
555 void init() {
556 LWIP_LOG("init(%p)", pcb_);
557 tcp_arg(pcb_, this);
558 tcp_accept(pcb_, LWIPRawListenImpl::s_accept_fn);
559 tcp_err(pcb_, LWIPRawImpl::s_err_fn); // Use base class error handler
560 }
561
562 std::unique_ptr<Socket> accept(struct sockaddr *addr, socklen_t *addrlen) override {
563 if (pcb_ == nullptr) {
564 errno = EBADF;
565 return nullptr;
566 }
567 if (accepted_socket_count_ == 0) {
568 errno = EWOULDBLOCK;
569 return nullptr;
570 }
571 // Take from front for FIFO ordering
572 std::unique_ptr<LWIPRawImpl> sock = std::move(accepted_sockets_[0]);
573 // Shift remaining sockets forward
574 for (uint8_t i = 1; i < accepted_socket_count_; i++) {
575 accepted_sockets_[i - 1] = std::move(accepted_sockets_[i]);
576 }
577 accepted_socket_count_--;
578 LWIP_LOG("Connection accepted by application, queue size: %d", accepted_socket_count_);
579 if (addr != nullptr) {
580 sock->getpeername(addr, addrlen);
581 }
582 LWIP_LOG("accept(%p)", sock.get());
583 return std::unique_ptr<Socket>(std::move(sock));
584 }
585
586 int listen(int backlog) override {
587 if (pcb_ == nullptr) {
588 errno = EBADF;
589 return -1;
590 }
591 LWIP_LOG("tcp_listen_with_backlog(%p backlog=%d)", pcb_, backlog);
592 struct tcp_pcb *listen_pcb = tcp_listen_with_backlog(pcb_, backlog);
593 if (listen_pcb == nullptr) {
594 tcp_abort(pcb_);
595 pcb_ = nullptr;
596 errno = EOPNOTSUPP;
597 return -1;
598 }
599 // tcp_listen reallocates the pcb, replace ours
600 pcb_ = listen_pcb;
601 // set callbacks on new pcb
602 LWIP_LOG("tcp_arg(%p)", pcb_);
603 tcp_arg(pcb_, this);
604 tcp_accept(pcb_, LWIPRawListenImpl::s_accept_fn);
605 return 0;
606 }
607
608 private:
609 err_t accept_fn(struct tcp_pcb *newpcb, err_t err) {
610 LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
611 if (err != ERR_OK || newpcb == nullptr) {
612 // "An error code if there has been an error accepting. Only return ERR_ABRT if you have
613 // called tcp_abort from within the callback function!"
614 // https://www.nongnu.org/lwip/2_1_x/tcp_8h.html#a00517abce6856d6c82f0efebdafb734d
615 // nothing to do here, we just don't push it to the queue
616 return ERR_OK;
617 }
618 // Check if we've reached the maximum accept queue size
619 if (accepted_socket_count_ >= MAX_ACCEPTED_SOCKETS) {
620 LWIP_LOG("Rejecting connection, queue full (%d)", accepted_socket_count_);
621 // Abort the connection when queue is full
622 tcp_abort(newpcb);
623 // Must return ERR_ABRT since we called tcp_abort()
624 return ERR_ABRT;
625 }
626 auto sock = make_unique<LWIPRawImpl>(family_, newpcb);
627 sock->init();
628 accepted_sockets_[accepted_socket_count_++] = std::move(sock);
629 LWIP_LOG("Accepted connection, queue size: %d", accepted_socket_count_);
630 return ERR_OK;
631 }
632
633 static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
634 LWIPRawListenImpl *arg_this = reinterpret_cast<LWIPRawListenImpl *>(arg);
635 return arg_this->accept_fn(newpcb, err);
636 }
637
638 // Accept queue - holds incoming connections briefly until the event loop calls accept()
639 // This is NOT a connection pool - just a temporary queue between LWIP callbacks and the main loop
640 // 3 slots is plenty since connections are pulled out quickly by the event loop
641 //
642 // Memory analysis: std::array<3> vs original std::queue implementation:
643 // - std::queue uses std::deque internally which on 32-bit systems needs:
644 // 24 bytes (deque object) + 32+ bytes (map array) + heap allocations
645 // Total: ~56+ bytes minimum, plus heap fragmentation
646 // - std::array<3>: 12 bytes fixed (3 pointers × 4 bytes)
647 // Saves ~44+ bytes RAM per listening socket + avoids ALL heap allocations
648 // Used on ESP8266 and RP2040 (platforms using LWIP_TCP implementation)
649 //
650 // By using a separate listening socket class, regular connected sockets save
651 // 16 bytes (12 bytes array + 1 byte count + 3 bytes padding) of memory overhead on 32-bit systems
652 static constexpr size_t MAX_ACCEPTED_SOCKETS = 3;
653 std::array<std::unique_ptr<LWIPRawImpl>, MAX_ACCEPTED_SOCKETS> accepted_sockets_;
654 uint8_t accepted_socket_count_ = 0; // Number of sockets currently in queue
655};
656
657std::unique_ptr<Socket> socket(int domain, int type, int protocol) {
658 auto *pcb = tcp_new();
659 if (pcb == nullptr)
660 return nullptr;
661 // Create listening socket implementation since user sockets typically bind+listen
662 // Accepted connections are created directly as LWIPRawImpl in the accept callback
663 auto *sock = new LWIPRawListenImpl((sa_family_t) domain, pcb); // NOLINT(cppcoreguidelines-owning-memory)
664 sock->init();
665 return std::unique_ptr<Socket>{sock};
666}
667
668std::unique_ptr<Socket> socket_loop_monitored(int domain, int type, int protocol) {
669 // LWIPRawImpl doesn't use file descriptors, so monitoring is not applicable
670 return socket(domain, type, protocol);
671}
672
673} // namespace socket
674} // namespace esphome
675
676#endif // USE_SOCKET_IMPL_LWIP_TCP
uint16_t type
uint16_t flags
uint16_t in_port_t
Definition headers.h:58
uint32_t socklen_t
Definition headers.h:97
uint8_t sa_family_t
Definition headers.h:57
__int64 ssize_t
Definition httplib.h:178
in_addr ip_addr_t
Definition ip_address.h:22
mopeka_std_values val[4]
std::unique_ptr< Socket > socket(int domain, int type, int protocol)
Create a socket of the given domain, type and protocol.
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.
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:500
uint8_t sin6_len
Definition headers.h:73
in_port_t sin6_port
Definition headers.h:75
struct in6_addr sin6_addr
Definition headers.h:77
sa_family_t sin6_family
Definition headers.h:74
struct in_addr sin_addr
Definition headers.h:65
uint8_t sin_len
Definition headers.h:62
sa_family_t sin_family
Definition headers.h:63
in_port_t sin_port
Definition headers.h:64