FastLED 3.9.15
Loading...
Searching...
No Matches
native_server.cpp.hpp
Go to the documentation of this file.
1#pragma once
2
3// This file requires native socket APIs (Windows or POSIX).
4// On embedded platforms (STM32, AVR, etc.) this file compiles to nothing.
5#ifdef FASTLED_HAS_NETWORKING
6
7// Platform-specific socket includes (provides normalized POSIX API)
8#ifdef FL_IS_WIN
9 #include "platforms/win/socket_win.h" // ok platform headers // IWYU pragma: keep
10#else
11 #include "platforms/posix/socket_posix.h" // ok platform headers // IWYU pragma: keep
12#endif
13
14// Now include FastLED headers
16#include "fl/stl/noexcept.h"
17
18namespace fl {
19
20NativeHttpServer::NativeHttpServer(u16 port, const ConnectionConfig& config)
21 : mPort(port)
22 , mIsListening(false)
23 , mNextClientId(1)
24 , mConfig(config)
25{
26}
27
28NativeHttpServer::~NativeHttpServer() FL_NOEXCEPT {
29 stop();
30}
31
32bool NativeHttpServer::start() {
33 if (mIsListening) {
34 return true; // Already listening
35 }
36
37 if (platformStartListening()) {
38 mIsListening = true;
39 return true;
40 }
41
42 return false;
43}
44
45void NativeHttpServer::stop() {
46 // Disconnect all clients
47 disconnectAllClients();
48
49 // Stop listening
50 platformStopListening();
51 mIsListening = false;
52}
53
54bool NativeHttpServer::isListening() const {
55 return mIsListening;
56}
57
58void NativeHttpServer::acceptClients() {
59 if (!mIsListening) {
60 return;
61 }
62
63 // Accept new clients in a loop (non-blocking)
64 while (true) {
65 ServerClientConnection conn;
66 conn.clientId = mNextClientId;
67 conn.connection = HttpConnection(mConfig);
68
69 asio::error_code ec = mAcceptor.accept(conn.socket);
70 if (ec) {
71 break; // No more clients to accept (would_block) or error
72 }
73
74 conn.connection.onConnected(0); // Mark as connected immediately
75 mNextClientId++;
76 mClients.push_back(fl::move(conn));
77 }
78}
79
80size_t NativeHttpServer::getClientCount() const {
81 return mClients.size();
82}
83
84bool NativeHttpServer::hasClient(u32 clientId) const {
85 return findClient(clientId) != nullptr;
86}
87
88void NativeHttpServer::disconnectClient(u32 clientId) {
89 removeClient(clientId);
90}
91
92void NativeHttpServer::disconnectAllClients() {
93 // tcp::socket closes automatically in destructor
94 mClients.clear();
95}
96
97int NativeHttpServer::send(u32 clientId, fl::span<const u8> data) {
98 ServerClientConnection* client = findClient(clientId);
99 if (!client || !client->socket.is_open()) {
100 return -1;
101 }
102
103 asio::error_code ec;
104 size_t n = client->socket.write_some(data, ec);
105
106 if (ec) {
107 if (ec.code == asio::errc::would_block) {
108 return 0;
109 }
110 // Connection error, disconnect client
111 client->connection.onDisconnected();
112 return -1;
113 }
114
115 return static_cast<int>(n);
116}
117
118int NativeHttpServer::recv(u32 clientId, fl::span<u8> buffer) {
119 ServerClientConnection* client = findClient(clientId);
120 if (!client || !client->socket.is_open()) {
121 return -1;
122 }
123
124 asio::error_code ec;
125 size_t n = client->socket.read_some(buffer, ec);
126
127 if (ec) {
128 if (ec.code == asio::errc::would_block) {
129 return 0; // Non-blocking socket, no data available
130 }
131 // Connection error or EOF, disconnect client
132 client->connection.onDisconnected();
133 return -1;
134 }
135
136 // Data received, update heartbeat tracking
137 client->connection.onHeartbeatReceived();
138
139 return static_cast<int>(n);
140}
141
142void NativeHttpServer::broadcast(fl::span<const u8> data) {
143 // Send to all clients
144 for (auto& client : mClients) {
145 send(client.clientId, data);
146 }
147}
148
149void NativeHttpServer::update(u32 currentTimeMs) {
150 // Check for dead connections
151 for (size_t i = 0; i < mClients.size(); ) {
152 auto& client = mClients[i];
153
154 // Update connection state
155 client.connection.update(currentTimeMs);
156
157 // Check if client is still connected
158 if (!client.connection.isConnected() || !isSocketConnected(client.socket)) {
159 // Remove disconnected client (socket closes in destructor)
160 mClients.erase(mClients.begin() + i);
161 continue;
162 }
163
164 ++i;
165 }
166}
167
168fl::vector<u32> NativeHttpServer::getClientIds() const {
169 fl::vector<u32> ids;
170 ids.reserve(mClients.size());
171 for (const auto& client : mClients) {
172 ids.push_back(client.clientId);
173 }
174 return ids;
175}
176
177bool NativeHttpServer::platformStartListening() {
178 asio::error_code ec = mAcceptor.open(mPort);
179 if (ec) {
180 return false;
181 }
182
183 // Update port in case 0 was requested
184 mPort = mAcceptor.port();
185
186 ec = mAcceptor.listen(10);
187 if (ec) {
188 mAcceptor.close();
189 return false;
190 }
191
192 return true;
193}
194
195void NativeHttpServer::platformStopListening() {
196 mAcceptor.close();
197}
198
199ServerClientConnection* NativeHttpServer::findClient(u32 clientId) {
200 for (auto& client : mClients) {
201 if (client.clientId == clientId) {
202 return &client;
203 }
204 }
205 return nullptr;
206}
207
208const ServerClientConnection* NativeHttpServer::findClient(u32 clientId) const {
209 for (const auto& client : mClients) {
210 if (client.clientId == clientId) {
211 return &client;
212 }
213 }
214 return nullptr;
215}
216
217void NativeHttpServer::removeClient(u32 clientId) {
218 for (size_t i = 0; i < mClients.size(); ++i) {
219 if (mClients[i].clientId == clientId) {
220 // Socket closes automatically in destructor
221 mClients.erase(mClients.begin() + i);
222 return;
223 }
224 }
225}
226
227bool NativeHttpServer::isSocketConnected(const asio::ip::tcp::socket& sock) const {
228 if (!sock.is_open()) {
229 return false;
230 }
231
232 // Check socket status using getsockopt
233 int error = 0;
234 socklen_t len = sizeof(error);
235 int ret = getsockopt(sock.native_handle(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
236
237 if (ret != 0 || error != 0) {
238 return false;
239 }
240
241 return true;
242}
243
244} // namespace fl
245
246#endif // FASTLED_HAS_NETWORKING
void reserve(fl::size n) FL_NOEXCEPT
Definition vector.h:591
void push_back(const T &value) FL_NOEXCEPT
Definition vector.h:624
constexpr remove_reference< T >::type && move(T &&t) FL_NOEXCEPT
Definition move.h:28
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT