Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Socket.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_Socket_h_
5#define _Stroika_Foundation_IO_Network_Socket_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10
11#if qStroika_Platform_POSIX
12#include <sys/socket.h>
13#endif
14
16#include "Stroika/Foundation/Common/Common.h"
18#include "Stroika/Foundation/Execution/Exceptions.h"
19#include "Stroika/Foundation/IO/Network/SocketAddress.h"
20#if qStroika_Platform_Windows
21#include "Stroika/Foundation/IO/Network/Platform/Windows/WinSock.h"
22#endif
23
24#if qStroika_Platform_Linux or qStroika_Platform_MacOS
25using IPPROTO = int;
26#endif
27
28/**
29 * \file
30 *
31 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
32 */
33
35
36 using Characters::String;
37
38 /**
39 * A Socket has two parts, a _IRep*, and a smart pointer wrapper (like shared_ptr <_IRep>).
40 * Users of the class interact only with the smart pointer wrapper.
41 *
42 * But for purposes of thread safety, and understanding object lifetime, its important
43 * to consider both.
44 *
45 * The Socket class itself is abstract - as you can only create particular kinds of sockets (like connection-oriented etc).
46 *
47 * And you almost always just interact with and manage the smart pointers - Socket::Ptr.
48 *
49 * But you construct a concrete subtype and assign it to a pointer.
50 *
51 * \par Example Usage
52 * \code
53 * Socket::Ptr s = ConnectionlessSocket{ SocketAddress::INET, Socket::DGRAM };
54 * \endcode
55 *
56 * The Socket smart pointer objects can be freely assigned and passed around, but the
57 * underlying (_IRep*) socket is finally disposed of when the last reference to it
58 * goes away (or when it is 'Closed').
59 *
60 * Closing one, closes them all (though overwriting one just has the effect of detaching
61 * from the underlying socket.
62 *
63 * TODO:
64 * @todo In socket class, set CLOSE_ON_EXEC?
65 *
66 * @todo Document (or define new exception) thrown when operation done on CLOSED socket.
67 * and actually handle all the nullptr cases...
68 *
69 * @todo See about socket 'connected' state, and the 'connect' operation.
70 * And see about send/recv() API - and document about only working when
71 * connected.
72 *
73 * \note \em Thread-Safety not constructable
74 */
75 namespace Socket {
76
77 /**
78 * Platform Socket descriptor - file descriptor on unix (something like this on windoze)
79 */
80#if qStroika_Platform_Windows
81 using PlatformNativeHandle = SOCKET;
82#else
84#endif
85 class _IRep;
86
87 /**
88 * 'second arg' to ::socket() call - socket type
89 */
90 enum class Type : int {
91 STREAM = SOCK_STREAM,
92 DGRAM = SOCK_DGRAM,
93 RAW = SOCK_RAW,
94 };
95 using Type::DGRAM;
96 using Type::RAW;
97 using Type::STREAM;
98
99 /**
100 */
101 struct BindFlags {
102 /**
103 * SO_REUSEADDR:
104 *
105 * From https://man7.org/linux/man-pages/man7/socket.7.html
106 *
107 * Indicates that the rules used in validating addresses
108 * supplied in a bind(2) call should allow reuse of local
109 * addresses. For AF_INET sockets this means that a socket
110 * may bind, except when there is an active listening socket
111 * bound to the address.
112 *
113 * \par What Stroika means by it - on every platform
114 * The address being reused is the SOCKET address (internet address AND port):
115 * o UDP: other sockets that also set this may bind the same socket address - as multicast listeners
116 * (SSDP, say) must, to share the well-known port. Multicast and broadcast datagrams reach all of them;
117 * a UNICAST datagram reaches just one, and which one the OS decides (Linux 7.0: the last bound;
118 * macOS 26 and Windows 11: the first - though Windows documents it as indeterminate). So a socket
119 * that must hear unicast should be the only one bound there; one that only sends should not bind at
120 * all; and one that wants only multicast should bind the group's own address where the OS allows it
121 * (Linux and macOS do, Windows does not), so it is never sent unicast.
122 * o TCP: the socket may bind a socket address that an old connection still holds in TIME_WAIT - so a
123 * server can restart at once. It does NOT let two live listeners share the address.
124 *
125 * That is what SO_REUSEADDR alone does on Linux. On Windows too - which is also more permissive for TCP: there
126 * it can let another socket take over an address in active use (SO_EXCLUSIVEADDRUSE is Windows' guard, not
127 * exposed here).
128 *
129 * \par macOS (BSD)
130 * BSD reads the 'address' in SO_REUSEADDR as the INTERNET address: alone, it lets a UDP socket share a port
131 * only with sockets bound to a DIFFERENT internet address (a wildcard and a specific one, say). Sharing the
132 * same socket address takes SO_REUSEPORT as well - so for UDP sockets, Stroika sets both (as libuv does). Not
133 * for TCP: there SO_REUSEADDR already gives the meaning above, and SO_REUSEPORT would go further, letting two
134 * live listeners share the address.
135 *
136 * \par Why there is no separate SO_REUSEPORT flag
137 * Beyond the BSD use above (folded in here), SO_REUSEPORT is a different feature: several cooperating sockets
138 * deliberately sharing one port, so the kernel can divide the work between them (Linux hands each new
139 * connection or unicast datagram to just one) - for multi-process servers, and for restarting with no moment
140 * when nothing is listening. Its meaning varies too much by OS to expose raw (Linux load-balances; plain BSD
141 * does not - FreeBSD has a separate SO_REUSEPORT_LB; Windows has none). If ever wanted, add it under a name
142 * that says what it does - fLoadBalanceAcrossSockets, say - mapped per OS (and unsupported where there is
143 * no equivalent). This flag would not need to change.
144 */
145 bool fSO_REUSEADDR{false};
146 };
147
148 /**
149 */
150 enum class ShutdownTarget {
151 eReads,
152 eWrites,
153 eBoth,
154
155 eDEFAULT = eBoth,
156
157 Stroika_Define_Enum_Bounds (eReads, eBoth)
158 };
159
160 /**
161 * \brief a smart pointer wrapper (like shared_ptr <_IRep>).
162 *
163 * Users of the class interact only with the smart pointer wrapper.
164 *
165 * But for purposes of thread safety, and understanding object lifetime, its important
166 * to consider both.
167 *
168 * And you almost always just interact with and manage the smart pointers - Socket::Ptr.
169 *
170 * But you construct a concrete subtype and assign it to a pointer.
171 *
172 * \par Example Usage
173 * \code
174 * Socket::Ptr s = ConnectionlessSocket{ SocketAddress::INET, Socket::DGRAM };
175 * \endcode
176 *
177 * \par Example Usage
178 * \code
179 * Socket::Ptr s;
180 * if (s == nullptr) {
181 * s = ConnectionlessSocket::New (SocketAddress::INET, Socket::DGRAM);
182 * }
183 * \endcode
184 *
185 * The Socket smart pointer objects can be freely assigned and passed around, but the
186 * underlying (_IRep*) socket is finally disposed of when the last reference to it
187 * goes away (or when it is 'Closed').
188 *
189 * Closing one, closes them all (though overwriting one just has the effect of detaching
190 * from the underlying socket.
191 *
192 * \note select: Socket has no select method: instead use Execution::WaitForIOReady which
193 * works transparently with sockets, sets of sockets, or other waitable objects.
194 *
195 * \note inherits from Socket just for inherited type definitions - no methods or data.
196 *
197 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
198 * o static_assert (totally_ordered<Ptr>);
199 * o Socket::Ptr objects are compared (relative or equality) by their underlying 'rep' object.
200 * This USED to be done by calling GetNativeSocket () so two separately attached sockets
201 * would compare equal. Now - we compare the underlying smart pointers. This is nearly always
202 * the same thing, but can be different in the case of multiple objects attached to the same
203 * socket. This is probably a better definition, and definitely more efficient.
204 *
205 * \note Since Socket::Ptr is a smart pointer, the constness of the methods depends on whether they modify the smart pointer itself, not
206 * the underlying thread object.
207 *
208 * \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>
209 */
210 class Ptr {
211 public:
212 /**
213 * Socket::Ptr maybe initialized with
214 * o ConnectionlessSocket::New ()
215 * o ConnectionOrientedStreamSocket::New ()
216 * o ConnectionOrientedMasterSocket::New ()
217 *
218 * \note unless you call @Detach() - socket is CLOSED in DTOR of rep, so when final reference goes away
219 */
220 Ptr (const Ptr& s) = default;
221 Ptr (Ptr&& s) noexcept = default;
222 Ptr (nullptr_t);
223
224 protected:
225 Ptr () = delete;
226 Ptr (shared_ptr<_IRep>&& rep);
227 Ptr (const shared_ptr<_IRep>& rep);
228
229 public:
230 ~Ptr () = default;
231
232 public:
233 /**
234 * reset () doesn't clear the data in the socket, or close the socket, but un-references the socket
235 * smart pointer. Only if this socket shared_ptr is the last reference to the underlying stream
236 * data does this reset () close the underlying socket.
237 *
238 * @see Close ()
239 */
240 nonvirtual void reset () noexcept;
241
242 public:
243 /**
244 */
245 nonvirtual Ptr& operator= (Ptr&& s) noexcept;
246 nonvirtual Ptr& operator= (const Ptr& s);
247
248 public:
249 /**
250 * Disassociate the underlying native socket and this smart ptr.
251 * If the socket is shared among various smart_ptr reps, this dissociates those as well.
252 *
253 * This is legal to call if *this == nullptr.
254 * \post (*this == nullptr)
255 *
256 * @see Close ()
257 */
258 nonvirtual PlatformNativeHandle Detach ();
259
260 public:
261 /**
262 * Return the first argument to the ::socket() call (address family) or the result of getsockopt (SOL_SOCKET, SO_DOMAIN)
263 */
264 nonvirtual SocketAddress::FamilyType GetAddressFamily () const;
265
266 public:
267 /**
268 * Return the second argument to the ::socket() call (type) or the result of getsockopt (SOL_SOCKET, SO_TYPE)
269 */
270 nonvirtual Type GetType () const;
271
272 public:
273 /**
274 * Associate this socket object with the given address.
275 *
276 * @todo SB an overload taking just a port, and defaults to INADDRANY with that port.
277 * Port is only thing really needed, but InternetAddress part provided as arg
278 * too in case you want to bind to a particular interface.
279 *
280 * @todo CLARIFY if a socket can be bound to more than one address (and what about unbind)?
281 *
282 * \note This is usually just used on a ConnectionOrientedMasterSocket but may also be used
283 * by a ConnectionlessSocket (e.g. with SSDP).
284 *
285 * @see POSIX bind()
286 */
287 nonvirtual void Bind (const SocketAddress& sockAddr, BindFlags bindFlags = BindFlags{});
288
289 public:
290 /**
291 * if bound (@see Bind ()) - to what local endpoint? Remember a computer can be multi-homed, and can be bound to ADDR_ANY, or a specific address (plus the port).
292 */
293 nonvirtual optional<IO::Network::SocketAddress> GetLocalAddress () const;
294
295 public:
296 /**
297 * @see GetAutomaticTCPDisconnectOnClose - if set - Close automatically calls Shutdown () for connection-oriented sockets.
298 *
299 * @see https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown(v=vs.110).aspx
300 * When using a connection-oriented Socket, always call the Shutdown method before closing the Socket.
301 * This ensures that all data is sent and received on the connected socket before it is closed
302 *
303 * @see http://pubs.opengroup.org/onlinepubs/9699919799/
304 * The shutdown() function shall cause all or part of a full-duplex connection on the socket
305 * associated with the file descriptor socket to be shut down.
306 *
307 * The shutdown() function disables subsequent send and/or receive operations on a socket,
308 * depending on the value of the how argument.
309 *
310 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740481(v=vs.85).aspx
311 * If the how parameter is SD_RECEIVE, subsequent calls to the recv function on the socket
312 * will be disallowed. This has no effect on the lower protocol layers. For TCP sockets,
313 * if there is still data queued on the socket waiting to be received, or data arrives subsequently,
314 * the connection is reset, since the data cannot be delivered to the user. For UDP sockets,
315 * incoming datagrams are accepted and queued. In no case will an ICMP error packet be generated.
316 *
317 * If the how parameter is SD_SEND, subsequent calls to the send function are disallowed.
318 * For TCP sockets, a FIN will be sent after all data is sent and acknowledged by the receiver.
319 *
320 * \note this is ignored if the socket is already closed.
321 */
322 nonvirtual void Shutdown (ShutdownTarget shutdownTarget = ShutdownTarget::eDEFAULT);
323
324 public:
325 /**
326 * Note that Socket::Ptr is an envelope class, and there could be multiple references to
327 * the same underlying platform socket. But this closes ALL of them. It does not
328 * remove the reference to the underlying shared socket rep however.
329 *
330 * \note this is safe and does nothing if *this == nullptr, or if the socket is already closed
331 *
332 * @see Detach
333 * @see reset ()
334 */
335 nonvirtual void Close () const;
336
337 public:
338 /**
339 * A socket can be open or closed. Open is roughly analogous to non-null. A socket once closed
340 * can never be 'Opened' - but you can assign a new Open socket to the Socket object.
341 *
342 * \note - not same as ==nullptr? to be open, must not == nullptr and underlying rep must be open.
343 *
344 * @see Close
345 */
346 nonvirtual bool IsOpen () const;
347
348 public:
349 /**
350 */
351 nonvirtual bool operator== (const Ptr& rhs) const;
352
353 public:
354 /**
355 */
356 nonvirtual strong_ordering operator<=> (const Ptr&) const;
357
358 public:
359 /**
360 * Return the native platform handle object associated with this socket
361 * (typically an integer file descriptor)
362 */
363 nonvirtual PlatformNativeHandle GetNativeSocket () const;
364
365 public:
366 /**
367 * Usually the return value is an int, but the caller must specify the right type. This is a simple,
368 * low level, wrapper on 'man 2 getsockopt'.
369 *
370 * \note this is usually unnecessary, somewhat dangerous (easy to mismatch types), and most common socket options
371 * can be accessed via other methods (e.g. GetMulticastTTL ())
372 *
373 * @see setsockopt
374 */
375 template <typename RESULT_TYPE>
376 nonvirtual RESULT_TYPE getsockopt (int level, int optname) const;
377
378 public:
379 /**
380 * This is a simple, low level, wrapper on 'man 2 setsockopt'.
381 *
382 * \note this is usually unnecessary, somewhat dangerous (easy to mismatch types), and most common socket options
383 * can be accessed via other methods (e.g. SetMulticastTTL ())
384 *
385 * \note It maybe advisable to be explicit about the ARG_TYPE in the template, as constants like '3' may get misinterpreted,
386 * and you must use the exactly correct type for the low level setsockopt API.
387 *
388 * @see getsockopt
389 */
390 template <typename ARG_TYPE>
391 nonvirtual void setsockopt (int level, int optname, ARG_TYPE arg) const;
392
393 public:
394 /**
395 * @see Characters::ToString ()
396 */
397 nonvirtual String ToString () const;
398
399 protected:
400 /**
401 */
402 nonvirtual shared_ptr<_IRep> _GetSharedRep () const;
403
404 protected:
405 /**
406 * \pre fRep_ != nullptr
407 */
408 nonvirtual _IRep& _ref () const;
409
410 protected:
411 /**
412 * \pre fRep_ != nullptr
413 */
414 nonvirtual const _IRep& _cref () const;
415
416 protected:
418
419 private:
420 shared_ptr<_IRep> fRep_;
421 };
422 static_assert (totally_ordered<Ptr>);
423
424 /**
425 */
426 class _IRep {
427 public:
428 virtual ~_IRep () = default;
429 virtual void Shutdown (ShutdownTarget shutdownTarget) = 0;
430 virtual void Close () = 0;
431 virtual PlatformNativeHandle Detach () = 0;
432 virtual optional<IO::Network::SocketAddress> GetLocalAddress () const = 0;
433 virtual SocketAddress::FamilyType GetAddressFamily () const = 0;
434 virtual PlatformNativeHandle GetNativeSocket () const = 0;
435 virtual void getsockopt (int level, int optname, void* optval, socklen_t* optvallen) const = 0;
436 virtual void setsockopt (int level, int optname, const void* optval, socklen_t optvallen) = 0;
437 };
438
439 namespace _Protected {
440 PlatformNativeHandle mkLowLevelSocket_ (SocketAddress::FamilyType family, Socket::Type socketKind, const optional<IPPROTO>& protocol);
441 tuple<PlatformNativeHandle, PlatformNativeHandle> mkLowLevelSocketPair_ (SocketAddress::FamilyType family, Socket::Type socketKind,
442 const optional<IPPROTO>& protocol);
443 }
444 };
445
446#if qStroika_Platform_Windows
447 /**
448 * If the argument value (return code from some WSA API call) is SOCKET_ERROR (or if T = SOCKET we check for INVALID_SOCKET)
449 * This function throws a system error code given by WSAGetLastError ()
450 *
451 * see docs for https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-accept
452 * or https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-bind
453 * for examples of functions this can be used to wrap.
454 */
455 template <typename INT_TYPE>
456 INT_TYPE ThrowWSASystemErrorIfSOCKET_ERROR (INT_TYPE returnCode)
457 requires (is_signed_v<INT_TYPE>);
459#endif
460
461}
462
463namespace Stroika::Foundation::Execution::WaitForIOReady_Support {
464
465 // Specialize to override GetSDKPollable
466 template <typename T>
467 struct WaitForIOReady_Traits;
468 template <>
469 struct WaitForIOReady_Traits<IO::Network::Socket::Ptr> {
470 using HighLevelType = IO::Network::Socket::Ptr;
471 static inline auto GetSDKPollable (const HighLevelType& t)
472 {
473 return t.GetNativeSocket ();
474 }
475 };
476
477}
478
479/*
480 ********************************************************************************
481 ***************************** Implementation Details ***************************
482 ********************************************************************************
483 */
484#include "Socket.inl"
485
486#endif /*_Stroika_Foundation_IO_Network_Socket_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
#define Stroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:481
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
a smart pointer wrapper (like shared_ptr <_IRep>).
Definition Socket.h:210
nonvirtual RESULT_TYPE getsockopt(int level, int optname) const
nonvirtual PlatformNativeHandle Detach()
Definition Socket.cpp:195
nonvirtual void Bind(const SocketAddress &sockAddr, BindFlags bindFlags=BindFlags{})
Definition Socket.cpp:212
nonvirtual void setsockopt(int level, int optname, ARG_TYPE arg) const
nonvirtual SocketAddress::FamilyType GetAddressFamily() const
Definition Socket.inl:62
nonvirtual void Shutdown(ShutdownTarget shutdownTarget=ShutdownTarget::eDEFAULT)
Definition Socket.inl:67
nonvirtual optional< IO::Network::SocketAddress > GetLocalAddress() const
Definition Socket.inl:57
nonvirtual PlatformNativeHandle GetNativeSocket() const
Definition Socket.inl:52
nonvirtual const _IRep & _cref() const
Definition Socket.inl:47
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)