Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
ConnectionOrientedStreamSocket.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_IO_Network_ConnectionOrientedStreamSocket_h_
5#define _Stroika_Foundation_IO_Network_ConnectionOrientedStreamSocket_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
10
11#include "Socket.h"
12
13namespace Stroika::Foundation::Memory {
14 class BLOB;
15}
16
18
19 /**
20 * \brief ConnectionOrientedStreamSocket is typically a tcp stream, either setup with Connect, or ConnectionOrientedMasterSocket::Accept ()
21 *
22 * \par Example Usage
23 * \code
24 * ConnectionOrientedStreamSocket::Ptr s = ConnectionOrientedStreamSocket::New (SocketAddress::INET, Socket::STREAM);
25 * s.Connect (someSocketAddress);
26 * \endcode
27 *
28 * \par Example Usage
29 * \code
30 * ConnectionOrientedMasterSocket::Ptr ms ConnectionOrientedMasterSocket::New (SocketAddress::INET, Socket::STREAM);
31 * ms.Bind (addr);
32 * ms.Listen (backlog);
33 * ConnectionOrientedStreamSocket::Ptr newConnection = ms.Accept ();
34 * \endcode
35 *
36 * \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>
37 */
38 namespace ConnectionOrientedStreamSocket {
39 using namespace Socket;
40
41 class _IRep;
42
43 /**
44 */
45 struct KeepAliveOptions {
46 bool fEnabled{};
47#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
48 optional<unsigned int> fMaxProbesSentBeforeDrop; // https://linux.die.net/man/7/tcp TCP_KEEPCNT
49 optional<Time::DurationSeconds> fTimeIdleBeforeSendingKeepalives; // https://linux.die.net/man/7/tcp TCP_KEEPIDLE
50 optional<Time::DurationSeconds> fTimeBetweenIndividualKeepaliveProbes; // https://linux.die.net/man/7/tcp TCP_KEEPINTVL
51#endif
52 /**
53 * @see Characters::ToString ();
54 */
55 nonvirtual Characters::String ToString () const;
56 };
57
58 /**
59 * \par Example Usage
60 * \code
61 * ConnectionOrientedStreamSocket::Ptr s = ConnectionOrientedStreamSocket::New (SocketAddress::INET, Socket::STREAM);
62 * s.Connect (someSocketAddress);
63 * Sequence<ConnectionOrientedStreamSocket::Ptr> l;
64 * l.push_back (s);
65 * \endcode
66 *
67 * \note Since ConnectionOrientedStreamSocket::Ptr is a smart pointer, the constness of the methods depends on whether they modify the smart pointer itself, not
68 * the underlying thread object.
69 *
70 * \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>
71 * Note that we make Read, ReadNonBlocking, Connect, and Write () all const, so that they can be each called at the same time from different threads as other methods
72 * like GetPeerAddress ().
73 *
74 * It is \em NOT however, legal to call Read (or ReadNonBlocking) at the same time from two different threads, nor Write at the same time from two
75 * different threads, nor Connect while already connected, nor Read/ReadNonBlocking/Write while not Connected (and so therefore not legal to call
76 * Connect while calling Read/Write).
77 */
78 class Ptr : public Socket::Ptr {
79 private:
80 using inherited = Socket::Ptr;
81
82 public:
83 /**
84 */
85 Ptr () = delete;
86 Ptr (nullptr_t);
87 Ptr (const Ptr& src) = default;
88 Ptr (Ptr&& src) = default;
89 Ptr (shared_ptr<_IRep>&& rep);
90 Ptr (const shared_ptr<_IRep>& rep);
91
92 public:
93 /**
94 */
95 nonvirtual Ptr& operator= (const Ptr& rhs) = default;
96 nonvirtual Ptr& operator= (Ptr&& rhs) = default;
97
98 public:
99 /**
100 * @see Socket::Close () - plus optionally handles GetAutomaticTCPDisconnectOnClose
101 *
102 * \note - you don't have to use this Close () - using the base class close does the same thing,
103 * but these docs are here just to make that more clear - the extra close functionality.
104 *
105 * @see GetAutomaticTCPDisconnectOnClose - if set - Close automatically calls Shutdown () for connection-oriented sockets.
106 */
107 nonvirtual void Close () const;
108
109 public:
110 /**
111 * \brief Connects to the argument sockAddr;
112 *
113 * If explicit timeout given, and the connection doesn't complete in time, TimeOutException will result.
114 * If no explicit timeout is given, the OS default setting for timeouts will be used.
115 *
116 * \note ***Cancelation Point***
117 */
118 nonvirtual void Connect (const SocketAddress& sockAddr) const;
119 nonvirtual void Connect (const SocketAddress& sockAddr, const Time::Duration& timeout) const;
120
121 public:
122 /**
123 * @todo Need timeout on this API? Or global (for instance) timeout?
124 *
125 * \note Though Read () is a const method, can be done concurrently with most other const ConnectionOrientedStreamSocket methods,
126 * It is illegal (because useless and confusing) to do two reads (or ReadNonBlocking) at the same time. Read and Write maybe
127 * done simultaneously, from different threads.
128 *
129 * \note ***Cancelation Point***
130 */
131 nonvirtual span<byte> Read (span<byte> into) const;
132
133 public:
134 /**
135 * \brief Non-blocking read: cross between AvailableToRead () and Read(): read as many bytes into 'into' bytes as will fit and are available
136 *
137 * \note Though ReadNonBlocking () is a const method, can be done concurrently with most other const ConnectionOrientedStreamSocket methods,
138 * it is illegal (because useless and confusing) to do two reads (or ReadNonBlocking) at the same time. Read and Write maybe
139 * done simultaneously, from different threads.
140 *
141 * \pre into.size () >= 1
142 */
143 nonvirtual optional<span<byte>> ReadNonBlocking (span<byte> into) const;
144
145 public:
146 /**
147 * \brief Non-blocking: return nullopt if no data available, 0 on known EOF, or number of bytes known available
148 */
149 nonvirtual optional<size_t> AvailableToRead () const;
150
151 public:
152 /**
153 * @todo Need timeout on this API? Or global (for instance) timeout?
154 */
155 nonvirtual void Write (span<const byte> data) const;
156
157 public:
158 /**
159 * If there is a socket connected to the other side, return that peer's socket address.
160 */
161 nonvirtual optional<IO::Network::SocketAddress> GetPeerAddress () const;
162
163 public:
164 /**
165 * Automatically call Shutdown () when closing socket, and Wait this number of seconds to receive the
166 * peer's close acknowledgment. If missing, don't automatically call Shutdown, nor do any waiting for the acknowledgment.
167 *
168 * @see SetAutomaticTCPDisconnectOnClose ()
169 */
170 nonvirtual optional<Time::DurationSeconds> GetAutomaticTCPDisconnectOnClose () const;
171
172 public:
173 /**
174 * @see GetAutomaticTCPDisconnectOnClose ()
175 */
176 nonvirtual void SetAutomaticTCPDisconnectOnClose (const optional<Time::DurationSeconds>& waitFor) const;
177
178 public:
179 /**
180 * See http://man7.org/linux/man-pages/man7/socket.7.html - option SO_LINGER
181 *
182 * If MISSING, then SO_LINGER is disabled - the default. This means a socket close
183 * will make best effort delivery, but close wont BLOCK until data delivered.
184 *
185 * If PRESENT, then the value is the number of seconds close will wait to finish
186 * delivering data.
187 *
188 * \note If you see (netstat -an) lots of TIME_WAIT state sockets, you can generally
189 * eliminate them with SetLinger (0); However, this is generally not advisable,
190 * as close() will send a RST (connection reset) which indicates an error condition.
191 * (see http://stackoverflow.com/questions/3757289/tcp-option-so-linger-zero-when-its-required)
192 *
193 * @see SetLinger ()
194 */
195 nonvirtual optional<int> GetLinger () const;
196
197 public:
198 /**
199 * @see GetLinger ()
200 */
201 nonvirtual void SetLinger (const optional<int>& linger) const;
202
203 public:
204 /**
205 * \brief Is this socket configured to use TCP keepalives (SO_KEEPALIVE)
206 *
207 * @see https://linux.die.net/man/3/setsockopt (SO_KEEPALIVE)
208 */
209 nonvirtual KeepAliveOptions GetKeepAlives () const;
210
211 public:
212 /**
213 * @see GetKeepAlives
214 * @see KeepAliveOptions
215 */
216 nonvirtual void SetKeepAlives (const KeepAliveOptions& keepalive) const;
217
218 public:
219 /**
220 * Is the TCP_NODELAY algorithm being used? Generally - this increases latency, for the benefit of better bandwidth utilization.
221 *
222 * See https://brooker.co.za/blog/2024/05/09/nagle.html for hints about if its right for you?
223 */
224 nonvirtual bool GetTCPNoDelay () const;
225
226 public:
227 /**
228 */
229 nonvirtual void SetTCPNoDelay (bool noDelay) const;
230
231 protected:
232 /**
233 */
234 nonvirtual shared_ptr<_IRep> _GetSharedRep () const;
235
236 protected:
237 /**
238 * \pre fRep_ != nullptr
239 */
240 nonvirtual _IRep& _ref () const;
241
242 protected:
243 /**
244 * \pre fRep_ != nullptr
245 */
246 nonvirtual const _IRep& _cref () const;
247
248 public:
249 [[deprecated ("Since Stroika v3.0d15 use span{} overload")]] size_t Read (byte* intoStart, byte* intoEnd) const
250 {
251 return Read (span{intoStart, intoEnd}).size ();
252 }
253 [[deprecated ("Since Stroika v3.0d15 - use span overload")]] void Write (const byte* start, const byte* end) const
254 {
255 Write (span{start, end});
256 }
257 [[deprecated ("Since Stroika v3.0d15 - use span overload")]] optional<size_t> ReadNonBlocking (byte* intoStart, byte* intoEnd) const
258 {
259 if (intoStart == nullptr) {
260 return this->AvailableToRead ();
261 }
262 if (auto o = ReadNonBlocking (span{intoStart, intoEnd})) {
263 return o->size ();
264 }
265 return nullopt;
266 }
267 };
268
269 /**
270 */
271 class _IRep : public Socket::_IRep {
272 public:
273 virtual ~_IRep () = default;
274 // return true on success, and false on failure (if e != nullptr) - if e == nullptr - just throws as normal
275 virtual void Connect (const SocketAddress& sockAddr, const optional<Time::Duration>& timeout) const = 0;
276 virtual span<byte> Read (span<byte> into) const = 0;
277 virtual optional<span<byte>> ReadNonBlocking (span<byte> into) const = 0;
278 virtual optional<size_t> AvailableToRead () const = 0;
279 virtual void Write (span<const byte> data) const = 0;
280 virtual optional<IO::Network::SocketAddress> GetPeerAddress () const = 0;
281 virtual optional<Time::DurationSeconds> GetAutomaticTCPDisconnectOnClose () const = 0;
282 virtual void SetAutomaticTCPDisconnectOnClose (const optional<Time::DurationSeconds>& waitFor) = 0;
283 virtual KeepAliveOptions GetKeepAlives () const = 0;
284 virtual void SetKeepAlives (const KeepAliveOptions& keepAliveOptions) = 0;
285 virtual bool GetTCPNoDelay () const = 0;
286 virtual void SetTCPNoDelay (bool noDelay) = 0;
287 };
288
289 /**
290 */
291 Ptr New (SocketAddress::FamilyType family, Type socketKind = Type::STREAM, const optional<IPPROTO>& protocol = {});
292
293 /**
294 * \brief create a ConnectionOrientedStreamSocket::Ptr, and connect to the given address.
295 *
296 * Shorthand for:
297 * ConnectionOrientedStreamSocket::Ptr s = ConnectionOrientedStreamSocket::New (sockAddr.GetAddressFamily (), Socket::STREAM);
298 * s.Connect (someSocketAddress);
299 * return s;
300 */
301 Ptr NewConnection (const SocketAddress& sockAddr);
302
303 /**
304 * \brief Create a pair of sockets which talk locally, directly to one another (both endpoints on this machine)
305 *
306 *
307 * \see https://man7.org/linux/man-pages/man2/socketpair.2.html
308 *
309 * \par Example Usage:
310 * \code
311 * // both pairs equal - and can use EITHER as from and either as 'to'
312 * auto [fromRawSocket, toRawSocket] = ConnectionOrientedStreamSocket::NewPair (SocketAddress::INET);
313 * auto fromStrm = TextToBinary::Writer::New (SocketStream::New (fromRawSocket));
314 * auto toStrm = BinaryToText::Reader::New (SocketStream::New (toRawSocket));
315 * fromStrm.Write ("Hello");
316 * fromStrm.Flush ();
317 * fromStrm.Close (); // Close socket, so ReadAll gets ALL
318 * auto readData = toStrm.ReadAll ();
319 * EXPECT_EQ (readData, "Hello");
320 *
321 * note could be implemented with ::socketpair() API, or equivalent to
322 * \code
323 * // Create a Listening master socket, bind it, and get it listening
324 * // Just needed temporarily to create the socketpair, then it can be closed when it goes out of scope
325 * auto connectionOrientedMaster = ConnectionOrientedMasterSocket::New (family, socketKind, protocol);
326 * connectionOrientedMaster.Bind (SocketAddress{LocalHost (family)});
327 * connectionOrientedMaster.Listen (1);
328 * // now make a NEW socket, with the bound address and connect;
329 * auto one = ConnectionOrientedStreamSocket::NewConnection (*connectionOrientedMaster.GetLocalAddress ());
330 * auto two = connectionOrientedMaster.Accept ();
331 * return make_tuple (one, two);
332 * \endcode
333 */
334 tuple<Ptr, Ptr> NewPair (SocketAddress::FamilyType family = SocketAddress::INET, Type socketKind = STREAM,
335 const optional<IPPROTO>& protocol = {});
336
337 /**
338 * This function associates a Platform native socket handle with a Stroika wrapper object.
339 *
340 * Once a PlatformNativeHandle is attached to Socket object, it will be automatically closed
341 * when the last reference to the socket disappears (or when someone calls close).
342 *
343 * To prevent that behavior, you can Detach the PlatformNativeHandle before destroying
344 * the associated Socket object.
345 */
347
348 };
349
350}
351
353
354 // Specialize to override GetSDKPollable
355 template <typename T>
356 struct WaitForIOReady_Traits;
357 template <>
358 struct WaitForIOReady_Traits<IO::Network::ConnectionOrientedStreamSocket::Ptr> {
360 static inline auto GetSDKPollable (const HighLevelType& t)
361 {
362 return t.GetNativeSocket ();
363 }
364 };
365
366}
367
368/*
369 ********************************************************************************
370 ***************************** Implementation Details ***************************
371 ********************************************************************************
372 */
373#include "ConnectionOrientedStreamSocket.inl"
374
375#endif /*_Stroika_Foundation_IO_Network_ConnectionOrientedStreamSocket_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual optional< size_t > AvailableToRead() const
Non-blocking: return nullopt if no data available, 0 on known EOF, or number of bytes known available...
nonvirtual void SetAutomaticTCPDisconnectOnClose(const optional< Time::DurationSeconds > &waitFor) const
nonvirtual optional< IO::Network::SocketAddress > GetPeerAddress() const
nonvirtual void SetKeepAlives(const KeepAliveOptions &keepalive) const
nonvirtual optional< Time::DurationSeconds > GetAutomaticTCPDisconnectOnClose() const
nonvirtual optional< span< byte > > ReadNonBlocking(span< byte > into) const
Non-blocking read: cross between AvailableToRead () and Read(): read as many bytes into 'into' bytes ...
nonvirtual KeepAliveOptions GetKeepAlives() const
Is this socket configured to use TCP keepalives (SO_KEEPALIVE)
nonvirtual void Connect(const SocketAddress &sockAddr) const
Connects to the argument sockAddr;.
a smart pointer wrapper (like shared_ptr <_IRep>).
Definition Socket.h:178
nonvirtual PlatformNativeHandle GetNativeSocket() const
Definition Socket.inl:52
FamilyType
Socket address family - also sometimes referred to as domain (argument to ::socket calls it domain)
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Ptr NewConnection(const SocketAddress &sockAddr)
create a ConnectionOrientedStreamSocket::Ptr, and connect to the given address.
tuple< Ptr, Ptr > NewPair(SocketAddress::FamilyType family=SocketAddress::INET, Type socketKind=STREAM, const optional< IPPROTO > &protocol={})
Create a pair of sockets which talk locally, directly to one another (both endpoints on this machine)