|
1 | 1 | #pragma once |
2 | 2 |
|
3 | | -#include <optional> |
| 3 | +#include <map> |
| 4 | +#include <thread> |
| 5 | +#include <atomic> |
4 | 6 | #include <future> |
| 7 | +#include <optional> |
5 | 8 | #include <string> |
| 9 | +#include <poll.h> |
6 | 10 |
|
7 | 11 | #include "Channel.h" |
| 12 | +#include "ChannelTx.h" |
| 13 | +#include "ClientsRepo.h" |
8 | 14 | #include "impl/ChannelUnixFile.h" |
| 15 | +#include "impl/ProgressObservableMutexImpl.h" |
9 | 16 |
|
10 | 17 | namespace styxlib |
11 | 18 | { |
@@ -88,4 +95,130 @@ namespace styxlib |
88 | 95 | std::future<void> disconnect(); |
89 | 96 | bool isConnected() const; |
90 | 97 | }; |
| 98 | + |
| 99 | + /** |
| 100 | + * Common base for socket-based server channels (TCP, UDP). |
| 101 | + * |
| 102 | + * Handles the shared lifecycle (start / stop / isStarted), the poll loop, |
| 103 | + * client bookkeeping, processBuffers, and cleanupClosedSockets. |
| 104 | + * |
| 105 | + * Subclasses must implement createServerSocket() to set up the protocol- |
| 106 | + * specific listen/bind socket. Subclasses may also override |
| 107 | + * acceptClients(), readDataFromSocket(), handlePollEvents(), |
| 108 | + * cleanupClosedSockets(), and sendBuffer() to customise per-protocol |
| 109 | + * behaviour (e.g. UDP does not call accept() and uses recvfrom/sendto). |
| 110 | + */ |
| 111 | + class ChannelUnixSocketServer : public ChannelRx, public ChannelTx |
| 112 | + { |
| 113 | + public: |
| 114 | + class Configuration |
| 115 | + { |
| 116 | + public: |
| 117 | + const uint16_t port; |
| 118 | + const std::shared_ptr<ClientsRepo> clientsRepo{nullptr}; |
| 119 | + const PacketHeaderSize packetSizeHeader{PacketHeaderSize::Size2Bytes}; |
| 120 | + const uint16_t iounit{8192}; |
| 121 | + const DeserializerL4Ptr deserializer{nullptr}; |
| 122 | + const uint16_t maxClients{16}; |
| 123 | + |
| 124 | + Configuration( |
| 125 | + uint16_t port, |
| 126 | + std::shared_ptr<ClientsRepo> clientsRepo, |
| 127 | + PacketHeaderSize packetSizeHeader, |
| 128 | + uint16_t iounit, |
| 129 | + DeserializerL4Ptr deserializer, |
| 130 | + uint8_t maxClients) |
| 131 | + : port(port), |
| 132 | + clientsRepo(clientsRepo), |
| 133 | + packetSizeHeader(packetSizeHeader), |
| 134 | + iounit(iounit), |
| 135 | + deserializer(deserializer), |
| 136 | + maxClients(maxClients) |
| 137 | + { |
| 138 | + } |
| 139 | + }; |
| 140 | + |
| 141 | + struct ClientInfo |
| 142 | + { |
| 143 | + ClientId id{0}; |
| 144 | + std::string address{""}; |
| 145 | + uint16_t port{0}; |
| 146 | + }; |
| 147 | + |
| 148 | + protected: |
| 149 | + class ClientFullInfo : public ClientInfo, public ReadBuffer |
| 150 | + { |
| 151 | + }; |
| 152 | + |
| 153 | + const Configuration configuration; |
| 154 | + std::thread serverThread; |
| 155 | + Socket serverSocketFd{InvalidFileDescriptor}; |
| 156 | + |
| 157 | + // Sockets / pseudo-sockets -> per-client state |
| 158 | + std::map<Socket, ClientFullInfo> socketToClientInfoMapFull; |
| 159 | + std::vector<Socket> socketsToClose; |
| 160 | + |
| 161 | + // Per-client transmit channels (used by connection-oriented protocols) |
| 162 | + std::map<ClientId, std::shared_ptr<ChannelUnixSocketTx>> clientIdToChannelClient; |
| 163 | + |
| 164 | + ProgressObservableMutexImpl<std::vector<ClientInfo>> clientsObserver; |
| 165 | + std::atomic<bool> running{false}; |
| 166 | + std::atomic<bool> stopRequested{false}; |
| 167 | + std::unique_ptr<std::promise<ErrorCode>> startPromise; |
| 168 | + std::vector<pollfd> pollFds; |
| 169 | + |
| 170 | + // ── Extension points ───────────────────────────────────────────────── |
| 171 | + /** |
| 172 | + * Create, configure and bind (and optionally listen on) the server |
| 173 | + * socket. Must return a valid fd on success or set startPromise with |
| 174 | + * an error code and return InvalidFileDescriptor on failure. |
| 175 | + */ |
| 176 | + virtual Socket createServerSocket() = 0; |
| 177 | + |
| 178 | + /** Called when the server-socket poll fd fires with POLLIN. |
| 179 | + * Default: calls accept() in a loop until EAGAIN. |
| 180 | + * Return true to indicate a new client was accepted (loop continues). */ |
| 181 | + virtual bool acceptClients(Socket serverSocket); |
| 182 | + |
| 183 | + /** Called when a client-socket poll fd fires with POLLIN. |
| 184 | + * Default: reads via recv() into the client's ReadBuffer. */ |
| 185 | + virtual void readDataFromSocket(Socket clientFd); |
| 186 | + |
| 187 | + /** Dispatch POLLIN / POLLERR / POLLHUP events for all ready fds. |
| 188 | + * Default implementation: server fd => acceptClients loop, |
| 189 | + * client fd => readDataFromSocket, |
| 190 | + * POLLERR|POLLHUP => mark socket for closure. */ |
| 191 | + virtual void handlePollEvents(Socket serverSocket, size_t numEvents); |
| 192 | + |
| 193 | + /** Remove sockets that were queued in socketsToClose. |
| 194 | + * Default: calls ::close(), removes from pollFds and maps. */ |
| 195 | + virtual void cleanupClosedSockets(); |
| 196 | + |
| 197 | + /** Process dirty ReadBuffers held in socketToClientInfoMapFull and |
| 198 | + * forward complete packets to the deserializer. Not virtual – the |
| 199 | + * framing logic is identical for all transports. */ |
| 200 | + void processBuffers(); |
| 201 | + |
| 202 | + void workThreadFunction(); |
| 203 | + |
| 204 | + /** Helper to rebuild and publish the clients observer vector. */ |
| 205 | + void publishClients(); |
| 206 | + |
| 207 | + /** Called at the end of stop() so subclasses can clean up protocol- |
| 208 | + * specific state (e.g. address maps in the UDP server). */ |
| 209 | + virtual void onStop() {} |
| 210 | + |
| 211 | + public: |
| 212 | + explicit ChannelUnixSocketServer(const Configuration &config); |
| 213 | + ~ChannelUnixSocketServer() override; |
| 214 | + |
| 215 | + SizeResult sendBuffer(ClientId clientId, const StyxBuffer buffer, Size size) override; |
| 216 | + std::future<ErrorCode> start(); |
| 217 | + std::future<void> stop(); |
| 218 | + bool isStarted() const; |
| 219 | + ProgressObserver<std::vector<ClientInfo>> &getClientsObserver() |
| 220 | + { |
| 221 | + return clientsObserver; |
| 222 | + } |
| 223 | + }; |
91 | 224 | } // namespace styxlib |
0 commit comments