Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Socket.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. 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_Foundation_Common_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_Foundation_Common_Platform_Windows
21#include "Stroika/Foundation/IO/Network/Platform/Windows/WinSock.h"
22#endif
23
24#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_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_Foundation_Common_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 bool fSO_REUSEADDR{false};
114 };
115
116 /**
117 */
118 enum class ShutdownTarget {
119 eReads,
120 eWrites,
121 eBoth,
122
123 eDEFAULT = eBoth,
124
125 Stroika_Define_Enum_Bounds (eReads, eBoth)
126 };
127
128 /**
129 * \brief a smart pointer wrapper (like shared_ptr <_IRep>).
130 *
131 * Users of the class interact only with the smart pointer wrapper.
132 *
133 * But for purposes of thread safety, and understanding object lifetime, its important
134 * to consider both.
135 *
136 * And you almost always just interact with and manage the smart pointers - Socket::Ptr.
137 *
138 * But you construct a concrete subtype and assign it to a pointer.
139 *
140 * \par Example Usage
141 * \code
142 * Socket::Ptr s = ConnectionlessSocket{ SocketAddress::INET, Socket::DGRAM };
143 * \endcode
144 *
145 * \par Example Usage
146 * \code
147 * Socket::Ptr s;
148 * if (s == nullptr) {
149 * s = ConnectionlessSocket::New (SocketAddress::INET, Socket::DGRAM);
150 * }
151 * \endcode
152 *
153 * The Socket smart pointer objects can be freely assigned and passed around, but the
154 * underlying (_IRep*) socket is finally disposed of when the last reference to it
155 * goes away (or when it is 'Closed').
156 *
157 * Closing one, closes them all (though overwriting one just has the effect of detaching
158 * from the underlying socket.
159 *
160 * \note select: Socket has no select method: instead use Execution::WaitForIOReady which
161 * works transparently with sockets, sets of sockets, or other waitable objects.
162 *
163 * \note inherits from Socket just for inherited type definitions - no methods or data.
164 *
165 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
166 * o static_assert (totally_ordered<Ptr>);
167 * o Socket::Ptr objects are compared (relative or equality) by their underlying 'rep' object.
168 * This USED to be done by calling GetNativeSocket () so two separately attached sockets
169 * would compare equal. Now - we compare the underlying smart pointers. This is nearly always
170 * the same thing, but can be different in the case of multiple objects attached to the same
171 * socket. This is probably a better definition, and definitely more efficient.
172 *
173 * \note Since Socket::Ptr is a smart pointer, the constness of the methods depends on whether they modify the smart pointer itself, not
174 * the underlying thread object.
175 *
176 * \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>
177 */
178 class Ptr {
179 public:
180 /**
181 * Socket::Ptr maybe initialized with
182 * o ConnectionlessSocket::New ()
183 * o ConnectionOrientedStreamSocket::New ()
184 * o ConnectionOrientedMasterSocket::New ()
185 *
186 * \note unless you call @Detach() - socket is CLOSED in DTOR of rep, so when final reference goes away
187 */
188 Ptr (const Ptr& s) = default;
189 Ptr (Ptr&& s) noexcept = default;
190 Ptr (nullptr_t);
191
192 protected:
193 Ptr () = delete;
194 Ptr (shared_ptr<_IRep>&& rep);
195 Ptr (const shared_ptr<_IRep>& rep);
196
197 public:
198 ~Ptr () = default;
199
200 public:
201 /**
202 * reset () doesn't clear the data in the socket, or close the socket, but un-references the socket
203 * smart pointer. Only if this socket shared_ptr is the last reference to the underlying stream
204 * data does this reset () close the underlying socket.
205 *
206 * @see Close ()
207 */
208 nonvirtual void reset () noexcept;
209
210 public:
211 /**
212 */
213 nonvirtual Ptr& operator= (Ptr&& s) noexcept;
214 nonvirtual Ptr& operator= (const Ptr& s);
215
216 public:
217 /**
218 * Disassociate the underlying native socket and this smart ptr.
219 * If the socket is shared among various smart_ptr reps, this dissociates those as well.
220 *
221 * This is legal to call if *this == nullptr.
222 * \post (*this == nullptr)
223 *
224 * @see Close ()
225 */
226 nonvirtual PlatformNativeHandle Detach ();
227
228 public:
229 /**
230 * Return the first argument to the ::socket() call (address family) or the result of getsockopt (SOL_SOCKET, SO_DOMAIN)
231 */
232 nonvirtual SocketAddress::FamilyType GetAddressFamily () const;
233
234 public:
235 /**
236 * Return the second argument to the ::socket() call (type) or the result of getsockopt (SOL_SOCKET, SO_TYPE)
237 */
238 nonvirtual Type GetType () const;
239
240 public:
241 /**
242 * Associate this socket object with the given address.
243 *
244 * @todo SB an overload taking just a port, and defaults to INADDRANY with that port.
245 * Port is only thing really needed, but InternetAddress part provided as arg
246 * too in case you want to bind to a particular interface.
247 *
248 * @todo CLARIFY if a socket can be bound to more than one address (and what about unbind)?
249 *
250 * \note This is usually just used on a ConnectionOrientedMasterSocket but may also be used
251 * by a ConnectionlessSocket (e.g. with SSDP).
252 *
253 * @see POSIX bind()
254 */
255 nonvirtual void Bind (const SocketAddress& sockAddr, BindFlags bindFlags = BindFlags{});
256
257 public:
258 /**
259 * 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).
260 */
261 nonvirtual optional<IO::Network::SocketAddress> GetLocalAddress () const;
262
263 public:
264 /**
265 * @see GetAutomaticTCPDisconnectOnClose - if set - Close automatically calls Shutdown () for connection-oriented sockets.
266 *
267 * @see https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.shutdown(v=vs.110).aspx
268 * When using a connection-oriented Socket, always call the Shutdown method before closing the Socket.
269 * This ensures that all data is sent and received on the connected socket before it is closed
270 *
271 * @see http://pubs.opengroup.org/onlinepubs/9699919799/
272 * The shutdown() function shall cause all or part of a full-duplex connection on the socket
273 * associated with the file descriptor socket to be shut down.
274 *
275 * The shutdown() function disables subsequent send and/or receive operations on a socket,
276 * depending on the value of the how argument.
277 *
278 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/ms740481(v=vs.85).aspx
279 * If the how parameter is SD_RECEIVE, subsequent calls to the recv function on the socket
280 * will be disallowed. This has no effect on the lower protocol layers. For TCP sockets,
281 * if there is still data queued on the socket waiting to be received, or data arrives subsequently,
282 * the connection is reset, since the data cannot be delivered to the user. For UDP sockets,
283 * incoming datagrams are accepted and queued. In no case will an ICMP error packet be generated.
284 *
285 * If the how parameter is SD_SEND, subsequent calls to the send function are disallowed.
286 * For TCP sockets, a FIN will be sent after all data is sent and acknowledged by the receiver.
287 *
288 * \note this is ignored if the socket is already closed.
289 */
290 nonvirtual void Shutdown (ShutdownTarget shutdownTarget = ShutdownTarget::eDEFAULT);
291
292 public:
293 /**
294 * Note that Socket::Ptr is an envelope class, and there could be multiple references to
295 * the same underlying platform socket. But this closes ALL of them. It does not
296 * remove the reference to the underlying shared socket rep however.
297 *
298 * \note this is safe and does nothing if *this == nullptr, or if the socket is already closed
299 *
300 * @see Detach
301 * @see reset ()
302 */
303 nonvirtual void Close () const;
304
305 public:
306 /**
307 * A socket can be open or closed. Open is roughly analogous to non-null. A socket once closed
308 * can never be 'Opened' - but you can assign a new Open socket to the Socket object.
309 *
310 * \note - not same as ==nullptr? to be open, must not == nullptr and underlying rep must be open.
311 *
312 * @see Close
313 */
314 nonvirtual bool IsOpen () const;
315
316 public:
317 /**
318 */
319 nonvirtual bool operator== (const Ptr& rhs) const;
320
321 public:
322 /**
323 */
324 nonvirtual strong_ordering operator<=> (const Ptr&) const;
325
326 public:
327 /**
328 * Return the native platform handle object associated with this socket
329 * (typically an integer file descriptor)
330 */
331 nonvirtual PlatformNativeHandle GetNativeSocket () const;
332
333 public:
334 /**
335 * Usually the return value is an int, but the caller must specify the right type. This is a simple,
336 * low level, wrapper on 'man 2 getsockopt'.
337 *
338 * \note this is usually unnecessary, somewhat dangerous (easy to mismatch types), and most common socket options
339 * can be accessed via other methods (e.g. GetMulticastTTL ())
340 *
341 * @see setsockopt
342 */
343 template <typename RESULT_TYPE>
344 nonvirtual RESULT_TYPE getsockopt (int level, int optname) const;
345
346 public:
347 /**
348 * This is a simple, low level, wrapper on 'man 2 setsockopt'.
349 *
350 * \note this is usually unnecessary, somewhat dangerous (easy to mismatch types), and most common socket options
351 * can be accessed via other methods (e.g. SetMulticastTTL ())
352 *
353 * \note It maybe advisable to be explicit about the ARG_TYPE in the template, as constants like '3' may get misinterpreted,
354 * and you must use the exactly correct type for the low level setsockopt API.
355 *
356 * @see getsockopt
357 */
358 template <typename ARG_TYPE>
359 nonvirtual void setsockopt (int level, int optname, ARG_TYPE arg) const;
360
361 public:
362 /**
363 * @see Characters::ToString ()
364 */
365 nonvirtual String ToString () const;
366
367 protected:
368 /**
369 */
370 nonvirtual shared_ptr<_IRep> _GetSharedRep () const;
371
372 protected:
373 /**
374 * \pre fRep_ != nullptr
375 */
376 nonvirtual _IRep& _ref () const;
377
378 protected:
379 /**
380 * \pre fRep_ != nullptr
381 */
382 nonvirtual const _IRep& _cref () const;
383
384 protected:
385 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex _fThisAssertExternallySynchronized;
386
387 private:
388 shared_ptr<_IRep> fRep_;
389 };
390 static_assert (totally_ordered<Ptr>);
391
392 /**
393 */
394 class _IRep {
395 public:
396 virtual ~_IRep () = default;
397 virtual void Shutdown (ShutdownTarget shutdownTarget) = 0;
398 virtual void Close () = 0;
399 virtual PlatformNativeHandle Detach () = 0;
400 virtual optional<IO::Network::SocketAddress> GetLocalAddress () const = 0;
401 virtual SocketAddress::FamilyType GetAddressFamily () const = 0;
402 virtual PlatformNativeHandle GetNativeSocket () const = 0;
403 virtual void getsockopt (int level, int optname, void* optval, socklen_t* optvallen) const = 0;
404 virtual void setsockopt (int level, int optname, const void* optval, socklen_t optvallen) = 0;
405 };
406
407 namespace _Protected {
408 PlatformNativeHandle mkLowLevelSocket_ (SocketAddress::FamilyType family, Socket::Type socketKind, const optional<IPPROTO>& protocol);
409 tuple<PlatformNativeHandle, PlatformNativeHandle> mkLowLevelSocketPair_ (SocketAddress::FamilyType family, Socket::Type socketKind,
410 const optional<IPPROTO>& protocol);
411 }
412 };
413
414#if qStroika_Foundation_Common_Platform_Windows
415 /**
416 * If the argument value (return code from some WSA API call) is SOCKET_ERROR (or if T = SOCKET we check for INVALID_SOCKET)
417 * This function throws a system error code given by WSAGetLastError ()
418 *
419 * see docs for https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-accept
420 * or https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-bind
421 * for examples of functions this can be used to wrap.
422 */
423 template <typename INT_TYPE>
424 INT_TYPE ThrowWSASystemErrorIfSOCKET_ERROR (INT_TYPE returnCode)
425 requires (is_signed_v<INT_TYPE>);
427#endif
428
429}
430
432
433 // Specialize to override GetSDKPollable
434 template <typename T>
435 struct WaitForIOReady_Traits;
436 template <>
437 struct WaitForIOReady_Traits<IO::Network::Socket::Ptr> {
438 using HighLevelType = IO::Network::Socket::Ptr;
439 static inline auto GetSDKPollable (const HighLevelType& t)
440 {
441 return t.GetNativeSocket ();
442 }
443 };
444
445}
446
447/*
448 ********************************************************************************
449 ***************************** Implementation Details ***************************
450 ********************************************************************************
451 */
452#include "Socket.inl"
453
454#endif /*_Stroika_Foundation_IO_Network_Socket_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
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:178
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)