Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
StaticUdpChannel.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <array>
10#include <cstddef>
11#include <cstdint>
12#include <cstring>
13#include <span>
14
15#if defined(_WIN32) || defined(_WIN64)
16#ifndef NOMINMAX
17#define NOMINMAX
18#endif
19#ifndef WIN32_LEAN_AND_MEAN
20#define WIN32_LEAN_AND_MEAN
21#endif
22#include <windows.h>
23#include <winsock2.h>
24#include <ws2tcpip.h>
25#pragma comment(lib, "ws2_32.lib")
26#define CORIUM_HAS_UDP_SOCKETS 1
27#elif __has_include(<sys/socket.h>) && __has_include(<netinet/in.h>) && __has_include(<arpa/inet.h>) && __has_include(<unistd.h>) && __has_include(<fcntl.h>)
28#include <arpa/inet.h>
29#include <fcntl.h>
30#include <netinet/in.h>
31#include <sys/socket.h>
32#include <unistd.h>
33#define CORIUM_HAS_UDP_SOCKETS 1
34#else
35#define CORIUM_HAS_UDP_SOCKETS 0
36#endif
37
41
42namespace corium::net {
43
47template <size_t MaxPacketSize = 512>
49public:
50 constexpr StaticUdpChannel() noexcept = default;
51
53 close();
54 }
55
58
60 : m_fd(other.m_fd) {
61 other.m_fd = -1;
62 }
63
65 if (this != &other) {
66 close();
67 m_fd = other.m_fd;
68 other.m_fd = -1;
69 }
70 return *this;
71 }
72
77 bool openAndBind(uint16_t port = 0, const char* ip = "0.0.0.0") noexcept {
78#if CORIUM_HAS_UDP_SOCKETS
79 close();
80
81#if defined(_WIN32) || defined(_WIN64)
82 WSADATA wsaData;
83 WSAStartup(MAKEWORD(2, 2), &wsaData);
84 SOCKET sock = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
85 if (sock == INVALID_SOCKET) {
86 return false;
87 }
88 m_fd = static_cast<int>(sock);
89#else
90 m_fd = ::socket(AF_INET, SOCK_DGRAM, 0);
91 if (m_fd < 0) {
92 return false;
93 }
94#endif
95
96 sockaddr_in addr{};
97 addr.sin_family = AF_INET;
98 addr.sin_port = htons(port);
99#if defined(_WIN32) || defined(_WIN64)
100 InetPtonA(AF_INET, ip, &addr.sin_addr);
101#else
102 inet_pton(AF_INET, ip, &addr.sin_addr);
103#endif
104
105 if (::bind(m_fd, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) < 0) {
106 close();
107 return false;
108 }
109 return true;
110#else
111 (void)port;
112 (void)ip;
113 return false;
114#endif
115 }
116
120 bool setNonBlocking(bool nonBlocking = true) noexcept {
121#if CORIUM_HAS_UDP_SOCKETS
122 if (m_fd < 0) {
123 return false;
124 }
125#if defined(_WIN32) || defined(_WIN64)
126 u_long mode = nonBlocking ? 1 : 0;
127 return ioctlsocket(static_cast<SOCKET>(m_fd), FIONBIO, &mode) == 0;
128#else
129 int flags = fcntl(m_fd, F_GETFL, 0);
130 if (flags < 0) return false;
131 flags = nonBlocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK);
132 return fcntl(m_fd, F_SETFL, flags) == 0;
133#endif
134#else
135 (void)nonBlocking;
136 return false;
137#endif
138 }
139
145 bool sendTo(const char* ip, uint16_t port, std::span<const uint8_t> data) noexcept {
146#if CORIUM_HAS_UDP_SOCKETS
147 if (m_fd < 0) {
148 // Lazy socket creation if not bound
149 if (!openAndBind(0, "0.0.0.0")) {
150 return false;
151 }
152 }
153
154 sockaddr_in destAddr{};
155 destAddr.sin_family = AF_INET;
156 destAddr.sin_port = htons(port);
157#if defined(_WIN32) || defined(_WIN64)
158 InetPtonA(AF_INET, ip, &destAddr.sin_addr);
159 int sent = ::sendto(
160 static_cast<SOCKET>(m_fd),
161 reinterpret_cast<const char*>(data.data()),
162 static_cast<int>(data.size()),
163 0,
164 reinterpret_cast<const sockaddr*>(&destAddr),
165 sizeof(destAddr)
166 );
167 return sent > 0 && static_cast<size_t>(sent) == data.size();
168#else
169 inet_pton(AF_INET, ip, &destAddr.sin_addr);
170 auto sent = ::sendto(
171 m_fd,
172 data.data(),
173 data.size(),
174 0,
175 reinterpret_cast<const sockaddr*>(&destAddr),
176 sizeof(destAddr)
177 );
178 return sent > 0 && static_cast<size_t>(sent) == data.size();
179#endif
180#else
181 (void)ip;
182 (void)port;
183 (void)data;
184 return false;
185#endif
186 }
187
195 template <typename Event, typename EventVariant>
196 bool sendEvent(const char* ip, uint16_t port, const Event& event) noexcept {
197 auto packet = corium::wire::WireSerializer::serialize<Event, EventVariant, MaxPacketSize>(event);
198 return sendTo(ip, port, std::span<const uint8_t>(
199 reinterpret_cast<const uint8_t*>(&packet), packet.totalWireSize()));
200 }
201
206 bool receive(std::span<uint8_t> bufferOut, size_t& bytesReceivedOut) noexcept {
207#if CORIUM_HAS_UDP_SOCKETS
208 if (m_fd < 0) {
209 bytesReceivedOut = 0;
210 return false;
211 }
212
213#if defined(_WIN32) || defined(_WIN64)
214 int recvd = ::recvfrom(
215 static_cast<SOCKET>(m_fd),
216 reinterpret_cast<char*>(bufferOut.data()),
217 static_cast<int>(bufferOut.size()),
218 0, nullptr, nullptr
219 );
220#else
221 auto recvd = ::recvfrom(
222 m_fd,
223 bufferOut.data(),
224 bufferOut.size(),
225 0, nullptr, nullptr
226 );
227#endif
228 if (recvd <= 0) {
229 bytesReceivedOut = 0;
230 return false;
231 }
232 bytesReceivedOut = static_cast<size_t>(recvd);
233 return true;
234#else
235 (void)bufferOut;
236 bytesReceivedOut = 0;
237 return false;
238#endif
239 }
240
247 template <typename EventVariant, typename Sink>
248 bool receiveAndPush(Sink& sink, EventPriority priority = EventPriority::Normal) noexcept {
249 size_t recvd = 0;
250 if (!receive(std::span<uint8_t>(m_rxBuffer.data(), m_rxBuffer.size()), recvd)) {
251 return false;
252 }
253
254 if (recvd < sizeof(corium::wire::WireHeader)) {
255 return false;
256 }
257
259 std::memcpy(&packet, m_rxBuffer.data(), recvd > sizeof(packet) ? sizeof(packet) : recvd);
260 return corium::wire::WireSerializer::deserializeAndPush<EventVariant, MaxPacketSize, Sink>(
261 packet, sink, priority);
262 }
263
265 void close() noexcept {
266#if CORIUM_HAS_UDP_SOCKETS
267 if (m_fd >= 0) {
268#if defined(_WIN32) || defined(_WIN64)
269 closesocket(static_cast<SOCKET>(m_fd));
270#else
271 ::close(m_fd);
272#endif
273 m_fd = -1;
274 }
275#endif
276 }
277
279 [[nodiscard]] bool isOpen() const noexcept {
280 return m_fd >= 0;
281 }
282
284 [[nodiscard]] int nativeHandle() const noexcept {
285 return m_fd;
286 }
287
288private:
289 int m_fd{-1};
290 std::array<uint8_t, sizeof(corium::wire::WireHeader) + MaxPacketSize> m_rxBuffer{};
291};
292
293} // namespace corium::net
Bounded and multi-tier priority MPSC queueing policies.
Type-safe serialization and direct event sink deserialization.
Binary packet framing with CRC-16 checksum and schema versioning.
Statically buffered, zero-heap UDP communication channel for distributed Corium nodes.
Definition StaticUdpChannel.hpp:48
StaticUdpChannel & operator=(StaticUdpChannel &&other) noexcept
Definition StaticUdpChannel.hpp:64
StaticUdpChannel & operator=(const StaticUdpChannel &)=delete
bool sendTo(const char *ip, uint16_t port, std::span< const uint8_t > data) noexcept
Send raw payload to target UDP endpoint.
Definition StaticUdpChannel.hpp:145
StaticUdpChannel(StaticUdpChannel &&other) noexcept
Definition StaticUdpChannel.hpp:59
bool setNonBlocking(bool nonBlocking=true) noexcept
Set socket non-blocking mode.
Definition StaticUdpChannel.hpp:120
bool openAndBind(uint16_t port=0, const char *ip="0.0.0.0") noexcept
Open UDP socket and bind to a local port and IP address.
Definition StaticUdpChannel.hpp:77
bool receive(std::span< uint8_t > bufferOut, size_t &bytesReceivedOut) noexcept
Receive raw bytes from incoming UDP datagram.
Definition StaticUdpChannel.hpp:206
bool sendEvent(const char *ip, uint16_t port, const Event &event) noexcept
Serialize and send a typed event over UDP using Corium WirePacket framing.
Definition StaticUdpChannel.hpp:196
int nativeHandle() const noexcept
Native socket file descriptor or handle.
Definition StaticUdpChannel.hpp:284
bool isOpen() const noexcept
Returns true if socket is open and bound.
Definition StaticUdpChannel.hpp:279
bool receiveAndPush(Sink &sink, EventPriority priority=EventPriority::Normal) noexcept
Receive a WirePacket and deserialize directly into a Corium EventSink.
Definition StaticUdpChannel.hpp:248
constexpr StaticUdpChannel() noexcept=default
StaticUdpChannel(const StaticUdpChannel &)=delete
void close() noexcept
Close underlying socket.
Definition StaticUdpChannel.hpp:265
Definition StaticUdpChannel.hpp:42
EventPriority
Event priority levels for multi-priority queue policies.
Definition QueuePolicies.hpp:32
DefaultEvents Event
Alias for DefaultEvents.
Definition Events.hpp:65
Header structure framing binary wire packets for serial, CAN, SPI, or network transport.
Definition WirePacket.hpp:49
Statically-sized zero-heap binary wire packet.
Definition WirePacket.hpp:64