ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
lwip_raw_tcp_impl.h
Go to the documentation of this file.
1#pragma once
3
4#ifdef USE_SOCKET_IMPL_LWIP_TCP
5
6#include <array>
7#include <cerrno>
8#include <cstring>
9#include <memory>
10#include <span>
11
13#include "headers.h"
14#include "lwip/ip.h"
15#include "lwip/netif.h"
16#include "lwip/opt.h"
17#include "lwip/tcp.h"
18
19namespace esphome::socket {
20
21// Forward declaration
22class LWIPRawImpl;
23
28 public:
29 LWIPRawCommon(sa_family_t family, struct tcp_pcb *pcb) : pcb_(pcb), family_(family) {}
31 LWIPRawCommon(const LWIPRawCommon &) = delete;
33
34 int bind(const struct sockaddr *name, socklen_t addrlen);
35 int close();
36 int shutdown(int how);
37
38 int getpeername(struct sockaddr *name, socklen_t *addrlen);
39 int getsockname(struct sockaddr *name, socklen_t *addrlen);
40
42 size_t getpeername_to(std::span<char, SOCKADDR_STR_LEN> buf);
44 size_t getsockname_to(std::span<char, SOCKADDR_STR_LEN> buf);
45
46 int getsockopt(int level, int optname, void *optval, socklen_t *optlen);
47 int setsockopt(int level, int optname, const void *optval, socklen_t optlen);
48
49 int get_fd() const { return -1; }
50
51 protected:
52 int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen);
53
54 // Member ordering optimized to minimize padding on 32-bit systems
55 struct tcp_pcb *pcb_;
56 // don't use lwip nodelay flag, it sometimes causes reconnect
57 // instead use it for determining whether to call lwip_output
58 bool nodelay_ = false;
60};
61
64class LWIPRawImpl : public LWIPRawCommon {
65 public:
68
69 void init();
70
71 // Non-listening sockets return error
72 std::unique_ptr<LWIPRawImpl> accept(struct sockaddr *, socklen_t *) {
73 errno = EINVAL;
74 return nullptr;
75 }
76 std::unique_ptr<LWIPRawImpl> accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen) {
77 return this->accept(addr, addrlen);
78 }
79 // Regular sockets can't be converted to listening - this shouldn't happen
80 // as listen() should only be called on sockets created for listening
81 int listen(int) {
82 errno = EOPNOTSUPP;
83 return -1;
84 }
85 ssize_t read(void *buf, size_t len);
86 ssize_t readv(const struct iovec *iov, int iovcnt);
87 ssize_t recvfrom(void *, size_t, sockaddr *, socklen_t *) {
88 errno = ENOTSUP;
89 return -1;
90 }
91 ssize_t write(const void *buf, size_t len);
92 ssize_t writev(const struct iovec *iov, int iovcnt);
93 ssize_t sendto(const void *, size_t, int, const struct sockaddr *, socklen_t) {
94 // return ::sendto(fd_, buf, len, flags, to, tolen);
95 errno = ENOSYS;
96 return -1;
97 }
98 bool ready() const { return this->rx_buf_ != nullptr || this->rx_closed_ || this->pcb_ == nullptr; }
99
100 int setblocking(bool blocking) {
101 if (this->pcb_ == nullptr) {
102 errno = ECONNRESET;
103 return -1;
104 }
105 if (blocking) {
106 // blocking operation not supported
107 errno = EINVAL;
108 return -1;
109 }
110 return 0;
111 }
112 int loop() { return 0; }
113
114 err_t recv_fn(struct pbuf *pb, err_t err);
115
116 static void s_err_fn(void *arg, err_t err);
117 static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err);
118
119 protected:
120 ssize_t internal_write_(const void *buf, size_t len);
121 int internal_output_();
122
123 pbuf *rx_buf_ = nullptr;
124 size_t rx_buf_offset_ = 0;
125 bool rx_closed_ = false;
126};
127
131 public:
134
135 void init();
136
137 bool ready() const { return this->accepted_socket_count_ > 0; }
138
139 std::unique_ptr<LWIPRawImpl> accept(struct sockaddr *addr, socklen_t *addrlen);
140 std::unique_ptr<LWIPRawImpl> accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen) {
141 return this->accept(addr, addrlen);
142 }
143 int listen(int backlog);
144
145 // Listening sockets don't do I/O
146 ssize_t read(void *, size_t) {
147 errno = ENOTSUP;
148 return -1;
149 }
150 ssize_t write(const void *, size_t) {
151 errno = ENOTSUP;
152 return -1;
153 }
154 ssize_t readv(const struct iovec *, int) {
155 errno = ENOTSUP;
156 return -1;
157 }
158 ssize_t writev(const struct iovec *, int) {
159 errno = ENOTSUP;
160 return -1;
161 }
162 ssize_t recvfrom(void *, size_t, sockaddr *, socklen_t *) {
163 errno = ENOTSUP;
164 return -1;
165 }
166 ssize_t sendto(const void *, size_t, int, const struct sockaddr *, socklen_t) {
167 errno = ENOTSUP;
168 return -1;
169 }
170 int setblocking(bool) { return 0; }
171 int loop() { return 0; }
172
173 static void s_err_fn(void *arg, err_t err);
174
175 private:
176 err_t accept_fn_(struct tcp_pcb *newpcb, err_t err);
177 static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err);
178
179 // Accept queue - holds incoming connections briefly until the event loop calls accept()
180 // This is NOT a connection pool - just a temporary queue between LWIP callbacks and the main loop
181 // 3 slots is plenty since connections are pulled out quickly by the event loop
182 //
183 // Memory analysis: std::array<3> vs original std::queue implementation:
184 // - std::queue uses std::deque internally which on 32-bit systems needs:
185 // 24 bytes (deque object) + 32+ bytes (map array) + heap allocations
186 // Total: ~56+ bytes minimum, plus heap fragmentation
187 // - std::array<3>: 12 bytes fixed (3 pointers × 4 bytes)
188 // Saves ~44+ bytes RAM per listening socket + avoids ALL heap allocations
189 // Used on ESP8266 and RP2040 (platforms using LWIP_TCP implementation)
190 //
191 // By using a separate listening socket class, regular connected sockets save
192 // 16 bytes (12 bytes array + 1 byte count + 3 bytes padding) of memory overhead on 32-bit systems
193 static constexpr size_t MAX_ACCEPTED_SOCKETS = 3;
194 std::array<std::unique_ptr<LWIPRawImpl>, MAX_ACCEPTED_SOCKETS> accepted_sockets_;
195 uint8_t accepted_socket_count_ = 0; // Number of sockets currently in queue
196};
197
198} // namespace esphome::socket
199
200#endif // USE_SOCKET_IMPL_LWIP_TCP
Non-virtual common base for LWIP raw TCP sockets.
int getsockname(struct sockaddr *name, socklen_t *addrlen)
size_t getsockname_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format local address into a fixed-size buffer (no heap allocation)
int bind(const struct sockaddr *name, socklen_t addrlen)
LWIPRawCommon(const LWIPRawCommon &)=delete
LWIPRawCommon & operator=(const LWIPRawCommon &)=delete
int ip2sockaddr_(ip_addr_t *ip, uint16_t port, struct sockaddr *name, socklen_t *addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
int getsockopt(int level, int optname, void *optval, socklen_t *optlen)
int getpeername(struct sockaddr *name, socklen_t *addrlen)
LWIPRawCommon(sa_family_t family, struct tcp_pcb *pcb)
size_t getpeername_to(std::span< char, SOCKADDR_STR_LEN > buf)
Format peer address into a fixed-size buffer (no heap allocation)
Connected socket implementation for LWIP raw TCP.
ssize_t recvfrom(void *, size_t, sockaddr *, socklen_t *)
static err_t s_recv_fn(void *arg, struct tcp_pcb *pcb, struct pbuf *pb, err_t err)
std::unique_ptr< LWIPRawImpl > accept(struct sockaddr *, socklen_t *)
ssize_t readv(const struct iovec *iov, int iovcnt)
static void s_err_fn(void *arg, err_t err)
ssize_t sendto(const void *, size_t, int, const struct sockaddr *, socklen_t)
std::unique_ptr< LWIPRawImpl > accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen)
err_t recv_fn(struct pbuf *pb, err_t err)
ssize_t internal_write_(const void *buf, size_t len)
ssize_t write(const void *buf, size_t len)
ssize_t read(void *buf, size_t len)
ssize_t writev(const struct iovec *iov, int iovcnt)
Listening socket implementation for LWIP raw TCP.
ssize_t recvfrom(void *, size_t, sockaddr *, socklen_t *)
static void s_err_fn(void *arg, err_t err)
std::unique_ptr< LWIPRawImpl > accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen)
ssize_t writev(const struct iovec *, int)
std::unique_ptr< LWIPRawImpl > accept(struct sockaddr *addr, socklen_t *addrlen)
ssize_t sendto(const void *, size_t, int, const struct sockaddr *, socklen_t)
ssize_t readv(const struct iovec *, int)
ssize_t write(const void *, size_t)
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
std::string size_t len
Definition helpers.h:817