Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
ConnectionlessSocket.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_ConnectionlessSocket_h_
5#define _Stroika_Foundation_IO_Network_ConnectionlessSocket_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <variant>
10
12
14
15 struct Interface;
16
17 /**
18 * \brief ConnectionlessSocket is typically a UDP socket you use for packet oriented communications (ie not tcp/streams)
19 *
20 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter">C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter</a>
21 */
22 namespace ConnectionlessSocket {
23 using namespace Socket;
24
25 class _IRep;
26
27 /**
28 * \brief Where a datagram arrived (from ReceiveFrom, once SetReceivePacketInfo (true)): the interface it came in on, and
29 * the address it was sent to - one of that interface's, a multicast group, or a broadcast address.
30 *
31 * What the OS reports with IP_PKTINFO (IPv4) / IPV6_PKTINFO (IPv6).
32 */
33 struct PacketInfo {
34 /**
35 * The interface it arrived on: its Interface::fIndex - which names it only while it exists, so look it up soon, not
36 * later (@see Interface::fIndex).
37 */
38 unsigned int fInterfaceIndex{};
39
40 /**
41 * The address it was sent to - its IP header's destination.
42 */
44
45 bool operator== (const PacketInfo&) const = default;
46
47 /**
48 * @see Characters::ToString ()
49 */
50 nonvirtual Characters::String ToString () const;
51 };
52
53 /**
54 * \par Example Usage
55 * \code
56 * ConnectionlessSocket::Ptr cs = ConnectionlessSocket::New (Socket::INET, Socket::DGRAM);
57 * \endcode
58 *
59 * \note Since ConnectionlessSocket::Ptr is a smart pointer, the constness of the methods depends on whether they modify the smart pointer itself, not
60 * the underlying thread object.
61 *
62 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter">C++-Standard-Thread-Safety-For-Envelope-But-Ambiguous-Thread-Safety-For-Letter</a>
63 */
64 class Ptr : public Socket::Ptr {
65 private:
66 using inherited = Socket::Ptr;
67
68 public:
69 /**
70 */
71 Ptr () = delete;
72 Ptr (nullptr_t);
73 Ptr (const Ptr& src) = default;
74 Ptr (Ptr&& src) = default;
75 Ptr (shared_ptr<_IRep>&& rep);
76 Ptr (const shared_ptr<_IRep>& rep);
77
78 public:
79 /**
80 */
81 nonvirtual Ptr& operator= (const Ptr& rhs) = default;
82 nonvirtual Ptr& operator= (Ptr&& rhs) = default;
83
84 public:
85 /**
86 * Join the multicast group iaddr (IPv4 or IPv6) - so this socket receives what is sent to it - on one interface: best
87 * given as an Interface (its index is looked up - by Interface::fInterfaceID - at the call), else by one of its
88 * addresses, or by its index (Interface::fIndex). V4::kAddrAny, V6::kAddrAny or Interface::kAnyIndex: the OS picks one.
89 *
90 * \note RACE: an index is right only for now - give the Interface, or get the index just before the call (and again
91 * for the matching LeaveMulticastGroup) - @see Interface::fIndex
92 *
93 * \note For IPv6, give an Interface (or index) rather than an address - a link-local address can be on more than one
94 * interface.
95 *
96 * \note macOS (as of 26.5) cannot pick the interface for a link-local-scope IPv6 group (ff02::...): it has no route
97 * to choose one by, so the join fails (EADDRNOTAVAIL) - name the interface.
98 */
99 nonvirtual void JoinMulticastGroup (const InternetAddress& iaddr, const Interface& onInterface) const;
100 nonvirtual void JoinMulticastGroup (const InternetAddress& iaddr, const InternetAddress& onInterface = V4::kAddrAny) const; ///< \brief join group iaddr on the interface with this address (V4::kAddrAny or V6::kAddrAny: the OS picks one)
101 nonvirtual void JoinMulticastGroup (const InternetAddress& iaddr, unsigned int onInterfaceIndex) const; ///< \brief join group iaddr on the interface whose Interface::fIndex this is (Interface::kAnyIndex: the OS picks one)
102
103 public:
104 /**
105 * Leave a group this socket joined (JoinMulticastGroup), on the interface it joined it on - best given as an Interface.
106 * Fails if there is no such membership - including when the interface went away since the join, as the OS drops a
107 * membership with its interface.
108 *
109 * \note RACE: given by index, look the index up again rather than reusing the one joined with - @see Interface::fIndex
110 */
111 nonvirtual void LeaveMulticastGroup (const InternetAddress& iaddr, const Interface& onInterface) const;
112 nonvirtual void LeaveMulticastGroup (const InternetAddress& iaddr, const InternetAddress& onInterface = V4::kAddrAny) const; ///< \brief leave group iaddr on the interface with this address
113 nonvirtual void LeaveMulticastGroup (const InternetAddress& iaddr, unsigned int onInterfaceIndex) const; ///< \brief leave group iaddr on the interface whose Interface::fIndex this is (Interface::kAnyIndex: the one the OS picked)
114
115 public:
116 /**
117 * Choose the interface multicast datagrams sent from this socket go out of - else the OS picks one, on a machine with
118 * several networks often not the one wanted. Best given as an Interface - looked up at the call: for an IPv6 socket
119 * its current index (by Interface::fInterfaceID), for an IPv4 socket one of its IPv4 addresses; else by one of
120 * its addresses, or (for an IPv6 socket) by its index (Interface::fIndex) - there is no portable way to choose an IPv4
121 * socket's by index.
122 *
123 * \note RACE: the socket keeps the choice as given - set it again when the interfaces change - @see Interface::fIndex
124 */
125 nonvirtual void SetMulticastInterface (const Interface& i) const;
126 nonvirtual void SetMulticastInterface (const InternetAddress& interfaceAddress) const; ///< \brief the interface with this address
127 nonvirtual void SetMulticastInterface (unsigned int interfaceIndex) const; ///< \brief IPv6 socket: the interface whose Interface::fIndex this is (Interface::kAnyIndex: the OS's default again)
128
129 public:
130 /**
131 * This specifies the number of networks to traverse in sending the multicast message.
132 * It defaults to 1.
133 */
134 nonvirtual uint8_t GetMulticastTTL () const;
135
136 public:
137 /**
138 * This specifies the number of networks to traverse in sending the multicast message.
139 * It defaults to 1.
140 */
141 nonvirtual void SetMulticastTTL (uint8_t ttl) const;
142
143 public:
144 /**
145 * This determines whether the data sent will be looped back to sender host or not.
146 */
147 nonvirtual bool GetMulticastLoopMode () const;
148
149 public:
150 /**
151 * This determines whether the data sent will be looped back to sender host or not.
152 */
153 nonvirtual void SetMulticastLoopMode (bool loopMode) const;
154
155 public:
156 /**
157 * Send the argument data to the argument socket address.
158 *
159 * @see https://linux.die.net/man/2/sendto
160 */
161 nonvirtual void SendTo (span<const byte> data, const SocketAddress& sockAddr) const;
162
163 public:
164 /**
165 * \brief Whether ReceiveFrom says where each datagram arrived (@see SetReceivePacketInfo)
166 */
167 nonvirtual bool GetReceivePacketInfo () const;
168
169 public:
170 /**
171 * \brief Have ReceiveFrom say where each datagram arrived (@see PacketInfo) - each arriving from now on - or stop. Off
172 * by default.
173 */
174 nonvirtual void SetReceivePacketInfo (bool on) const;
175
176 public:
177 /**
178 * Read the next message (typically a full packet) from the socket.
179 *
180 * @see https://linux.die.net/man/2/recvfrom
181 *
182 * if fromAddress != nullptr (legal to pass nullptr) - then it it is filled in with the source address the packet came from.
183 *
184 * returns a subspan (initial segment) of into, of length number of bytes read in packet.
185 *
186 * @todo DOCUMENT WHAT HAPPENS IF PACKET DOESNT FIT IN BUF!????
187 *
188 * @todo Let SendTo pick the interface (and source address) a datagram goes out of -
189 * https://github.com/SophistSolutions/Stroika/issues/1202
190 *
191 * \note ***Cancelation Point***
192 */
193 nonvirtual span<byte> ReceiveFrom (span<byte> into, int flag, SocketAddress* fromAddress, Time::DurationSeconds timeout = Time::kInfinity) const;
194 nonvirtual span<byte> ReceiveFrom (span<byte> into, int flag, SocketAddress* fromAddress, optional<PacketInfo>* packetInfo,
195 Time::DurationSeconds timeout = Time::kInfinity) const; ///< \brief ... and, if packetInfo != nullptr, where it arrived: nullopt unless SetReceivePacketInfo (true) came before it did
196
197 protected:
198 /**
199 */
200 nonvirtual shared_ptr<_IRep> _GetSharedRep () const;
201
202 protected:
203 /**
204 * \pre fRep_ != nullptr
205 */
206 nonvirtual _IRep& _ref () const;
207
208 protected:
209 /**
210 * \pre fRep_ != nullptr
211 */
212 nonvirtual const _IRep& _cref () const;
213
214 public:
215 [[deprecated ("Since Stroika 3.0d15 use span{} overload")]] size_t ReceiveFrom (byte* intoStart, byte* intoEnd, int flag,
216 SocketAddress* fromAddress,
217 Time::DurationSeconds timeout = Time::kInfinity) const
218 {
219 return ReceiveFrom (span{intoStart, intoEnd}, flag, fromAddress, timeout).size ();
220 }
221 [[deprecated ("Since Stroika v3.0d15 use span overload")]] void SendTo (const byte* start, const byte* end, const SocketAddress& sockAddr) const
222 {
223 SendTo (span<const byte>{start, end}, sockAddr);
224 }
225 };
226
227 /**
228 */
229 class _IRep : public Socket::_IRep {
230 public:
231 virtual ~_IRep () = default;
232
233 virtual void SendTo (const byte* start, const byte* end, const SocketAddress& sockAddr) = 0;
234 virtual size_t ReceiveFrom (byte* intoStart, byte* intoEnd, int flag, SocketAddress* fromAddress,
235 optional<PacketInfo>* packetInfo, Time::DurationSeconds timeout) = 0;
236 virtual bool GetReceivePacketInfo () const = 0;
237 virtual void SetReceivePacketInfo (bool on) = 0;
238 // the interface: by address or by index (as the Ptr's overloads take it)
239 virtual void JoinMulticastGroup (const InternetAddress& iaddr, const variant<InternetAddress, unsigned int>& onInterface) = 0;
240 virtual void LeaveMulticastGroup (const InternetAddress& iaddr, const variant<InternetAddress, unsigned int>& onInterface) = 0;
241 virtual void SetMulticastInterface (const variant<InternetAddress, unsigned int>& onInterface) = 0;
242 virtual uint8_t GetMulticastTTL () const = 0;
243 virtual void SetMulticastTTL (uint8_t ttl) = 0;
244 virtual bool GetMulticastLoopMode () const = 0;
245 virtual void SetMulticastLoopMode (bool loopMode) = 0;
246 };
247
248 /**
249 * \par Example Usage
250 * \code
251 * ConnectionlessSocket::Ptr s = ConnectionlessSocket::New (SocketAddress::INET, Socket::DGRAM);
252 * \endcode
253 *
254 * \pre socketKind != SOCK_STREAM
255 *
256 * \note unless you call @Detach() - socket is CLOSED in DTOR of rep, so when final reference goes away
257 *
258 * \note ConnectionlessSocket is not copyable, but it can be copied into a ConnectionlessSocket::Ptr or
259 * Socket::Ptr. This is critical to save them in a container, for example.
260 */
261 ConnectionlessSocket::Ptr New (SocketAddress::FamilyType family, Type socketKind, const optional<IPPROTO>& protocol = nullopt);
262
263 /**
264 * This function associates a Platform native socket handle with a Stroika wrapper object.
265 *
266 * Once a PlatformNativeHandle is attached to Socket object, it will be automatically closed
267 * when the last reference to the socket disappears (or when someone calls close).
268 *
269 * To prevent that behavior, you can Detach the PlatformNativeHandle before destroying
270 * the associated Socket object.
271 */
273
274 };
275
276}
277
278namespace Stroika::Foundation::Execution::WaitForIOReady_Support {
279
280 // Specialize to override GetSDKPollable
281 template <typename T>
282 struct WaitForIOReady_Traits;
283 template <>
284 struct WaitForIOReady_Traits<IO::Network::ConnectionlessSocket::Ptr> {
285 using HighLevelType = IO::Network::ConnectionlessSocket::Ptr;
286 static inline auto GetSDKPollable (const HighLevelType& t)
287 {
288 return t.GetNativeSocket ();
289 }
290 };
291
292}
293
294/*
295 ********************************************************************************
296 ***************************** Implementation Details ***************************
297 ********************************************************************************
298 */
299#include "ConnectionlessSocket.inl"
300
301#endif /*_Stroika_Foundation_IO_Network_ConnectionlessSocket_h_*/
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual void SendTo(span< const byte > data, const SocketAddress &sockAddr) const
nonvirtual void LeaveMulticastGroup(const InternetAddress &iaddr, const Interface &onInterface) const
nonvirtual void SetMulticastInterface(const Interface &i) const
nonvirtual void SetReceivePacketInfo(bool on) const
Have ReceiveFrom say where each datagram arrived (.
nonvirtual bool GetReceivePacketInfo() const
Whether ReceiveFrom says where each datagram arrived (.
nonvirtual span< byte > ReceiveFrom(span< byte > into, int flag, SocketAddress *fromAddress, Time::DurationSeconds timeout=Time::kInfinity) const
nonvirtual void JoinMulticastGroup(const InternetAddress &iaddr, const Interface &onInterface) const
a smart pointer wrapper (like shared_ptr <_IRep>).
Definition Socket.h:210
nonvirtual PlatformNativeHandle GetNativeSocket() const
Definition Socket.inl:52
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
ConnectionlessSocket::Ptr New(SocketAddress::FamilyType family, Type socketKind, const optional< IPPROTO > &protocol=nullopt)
Where a datagram arrived (from ReceiveFrom, once SetReceivePacketInfo (true)): the interface it came ...