3//#include <randolf/rex>
4//#include <randolf/rsocket>
5#include <randolf/rsocket_group_rc>
6#include <randolf/rtools>
10 /*======================================================================*//**
12 An rsocket_group provides a means to associate multiple @ref rsocket objects
13 so that the same operation can be performed on all of them at once.
15 The @ref rsocket::accept() and @ref rsocket::accept4() methods can also be
16 configured to automatically assign group membership to new connections, which
17 can be particularly useful for certain types of multi-user systems.
20 This class is threadsafe.
21 @author Randolf Richardson
24 - 2022-Dec-29 v1.00 Initial version
25 - 2025-Feb-03 v1.00 Increased use of references and pointers
26 - 2025-Feb-09 v1.00 In keeping with changes in rsocket, changed most
27 methods that return data wrapped in @c std::shared_ptr
28 to return data wrapped in @c std::unique_ptr instead
29 because this has a lot less overhead, and converting
30 from unique_ptr to shared_ptr is trivial, but the other
31 way around is not directly supported
32 - 2025-Feb-17 v1.00 Updated @ref net_io and related methods to include
33 support for the @c bytes_sx and @c crypt_sx statistics
34 *///=========================================================================
38 // --------------------------------------------------------------------------
39 // Comparator for use with std::set<rsocket> initialization. It simply
40 // compares the physical address of the instantiated rsocket() object since
41 // this is immediately available for the fastest possible comparison.
42 // --------------------------------------------------------------------------
43 template<typename R> struct __rsocket_cmp {
44 bool operator() (const std::weak_ptr<rsocket> r1, const std::weak_ptr<rsocket> r2) const {
45 auto _r1 = r1.lock(); if (!_r1) return false; // Expired pointer; we're done
46 auto _r2 = r2.lock(); if (!_r1) return true; // Not expired-after-expired pointer
47 return (uintptr_t)_r1.get() < (uintptr_t)_r2.get(); // Compare pointers as uint values
48 } // -x- bool operator() -x-
49 }; // -x- struct __rsocket_cmp -x-
51 // --------------------------------------------------------------------------
52 // Internal variables.
53 // --------------------------------------------------------------------------
54 std::mutex __mutex; // Used to ensure thread safety
55 std::set<std::weak_ptr<rsocket>, __rsocket_cmp<rsocket>> __r; // Set of rsocket objects (with our comparator)
57 // --------------------------------------------------------------------------
58 // Keep track of test for whether this host stores integers using big endian.
59 // --------------------------------------------------------------------------
60 inline static const bool __endian_is_msb = htons(42) == 42; // TRUE==MSB / FALSE==LSB
62 // --------------------------------------------------------------------------
63 // General variables. Atomic variables are used for variables as needed to
64 // support operations in a thread-safe manner.
65 // --------------------------------------------------------------------------
69 /*======================================================================*//**
71 Instantiate an rsocket group with no @ref rsocket objects.
74 *///=========================================================================
75 rsocket_group() noexcept {} // -x- constructor rsocket_group -x-
77 /*======================================================================*//**
79 Instantiate an rsocket group with any number of @ref rsocket objects.
82 *///=========================================================================
83 template<class R, class... Rs> rsocket_group(
84 /// Pointer to instantiated rsocket object
86 /// Variadic arguments (any quantity of instantiated rsocket objects)
90 } // -x- constructor rsocket_group -x-
93 /*======================================================================*//**
95 Internal method that is used by the @ref insert(rsocket*) method.
98 This method is not threadsafe.
99 *///=========================================================================
100 rsocket_group& __insert(
101 /// Pointer to instantiated rsocket object
102 rsocket* r) noexcept {
103 if (r == nullptr) return *this;
104 __r.insert(std::make_shared<rsocket>(r)); // Effectively: std::make_weak()
106 } // -x- rsocket_group& __insert -x-
108 /*======================================================================*//**
110 Internal method that is used by the @ref insert(rsocket*, ...) method.
113 This method is not threadsafe.
114 *///=========================================================================
115 template<class R, class... Rs> void __insert(
116 /// Pointer to instantiated rsocket object
118 /// Variadic arguments (any quantity of instantiated rsocket objects)
122 } // -x- void __insert -x-
125 /*======================================================================*//**
127 Close all rsockets that are in this rsocket_group.
128 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
129 containing successful operations that also record the number of
130 bytes that were transmitted, and the second one containing failed
131 operations that also record the exception that was thrown (see the
132 @ref rsocket_group_rc structure for details)
133 @see rsocket::close()
134 *///=========================================================================
135 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& close() {
137 // --------------------------------------------------------------------------
138 // Internal variables.
139 // --------------------------------------------------------------------------
140 std::vector<rsocket_group_rc>* v1 = new std::vector<rsocket_group_rc>();
141 std::vector<rsocket_group_rc>* v2 = new std::vector<rsocket_group_rc>();
142 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>* pair = new std::pair(*v1, *v2);
144 // --------------------------------------------------------------------------
145 // Loop through all rsocket objects in the underlying std::set.
147 // We don't need a mutex lock here because the close() method is threadsafe.
148 // --------------------------------------------------------------------------
149 for (std::weak_ptr<rsocket> wp : __r) {
150 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
153 v1->push_back({r.get(), 0, nullptr});
154 } catch (std::exception* e) {
155 v2->push_back({r.get(), 0, e});
157 } // -x- if r.lock() -x-
158 } // -x- foreach __r -x-
161 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& close -x-
163 /*======================================================================*//**
165 Remove an rsocket object from the underlying std::set.
168 This method is threadsafe.
169 *///=========================================================================
170 rsocket_group& erase(
171 /// Pointer to instantiated rsocket object
174 const std::lock_guard<std::mutex> lock(__mutex);
175 __r.erase(std::make_shared<rsocket>(r)); // Effectively: std::make_weak()
178 } // -x- rsocket_group& erase -x-
180 /*======================================================================*//**
182 Add one rsocket object to the underlying std::set.
185 This method is threadsafe.
186 *///=========================================================================
187 rsocket_group& insert(
188 /// Pointer to instantiated rsocket object
190 const std::lock_guard<std::mutex> lock(__mutex);
193 } // -x- rsocket_group& insert -x-
195 /*======================================================================*//**
197 Add one or more rsocket objects to the underlying std::set.
200 This method is threadsafe.
201 *///=========================================================================
202 template<class R, class... Rs> rsocket_group& insert(
203 /// Pointer to instantiated rsocket object
205 /// Variadic arguments (any quantity of instantiated rsocket objects)
207 const std::lock_guard<std::mutex> lock(__mutex);
211 } // -x- rsocket_group& insert -x-
213 /*======================================================================*//**
215 Specify a name for this rsocket_group.
217 This is an arbitrary name that is entirely optional, and should be regarded
218 as similar to the naming of threads.
221 This method is threadsafe.
222 *///=========================================================================
224 /// Name to assign to this rsocket_group
225 const std::string& name) noexcept {
226 const std::lock_guard<std::mutex> lock(__mutex);
229 } // -x- rsocket_group& name -x-
231 /*======================================================================*//**
233 Find out what this rsocket_group's name is.
236 This method is threadsafe.
237 @returns The name of this rsocket (or an empty std::string if this rsocket
239 *///=========================================================================
240 std::string name() noexcept {
241 const std::lock_guard<std::mutex> lock(__mutex);
243 } // -x- std::string name -x-
245 /*======================================================================*//**
247 Get the combined total of socket I/O statistics from all rsocket objects in
250 The number of bytes transmitted and received is tracked internally, so that
251 the information can be used later in logging. These statistics are available
252 at all times, but for logging purposes it makes the most sense to copy this
253 information after the rsocket is closed, at which time the final statistics
254 will continue to be available until the rsocket's destructor takes over.
257 This method is threadsafe.
258 @returns rsocket_io wrapped in a std::unique_ptr object to help ease memory
260 @see rsocket::net_io()
261 *///=========================================================================
262 std::unique_ptr<rsocket_io> net_io() noexcept {
263 std::unique_ptr stats = std::make_unique<rsocket_io>();
265 // --------------------------------------------------------------------------
266 // Loop through all rsocket objects in the underlying std::set.
268 // We don't need a mutex lock here because the stastical rx/tx counters in
269 // every rsocket class are atomic variables.
270 // --------------------------------------------------------------------------
271 for (std::weak_ptr<rsocket> wp : __r) {
272 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
273 stats->bytes_rx += r->__bytes_rx;
274 stats->bytes_tx += r->__bytes_tx;
275 stats->bytes_sx += r->__tls ? 0 : (r->__buffer != nullptr ? r->__buffer->get_utilized() : 0); // <bytes in buffer; are these lost bytes that should be subtracted from __bytes_rx?>
276 stats->crypt_rx += r->__crypt_rx;
277 stats->crypt_tx += r->__crypt_tx;
278 stats->crypt_sx += r->__tls ? (r->__buffer != nullptr ? r->__buffer->get_utilized() : 0) : 0; // <bytes in buffer; are these lost bytes that should be subtracted from __crypt_rx?>
279 } // -x- if r.lock() -x-
280 } // -x- foreach __r -x-
283 } // -x- std::unique_ptr<rsocket_io> net_io -x-
285 /*======================================================================*//**
287 Get combined total of socket I/O statistics from all rsocket objects in this
288 group as a pre-formatted std::string object.
290 See the @ref rsocket::net_io method for full documentation.
293 This method is threadsafe.
294 @returns An interpolated format string as an std::string object.
295 @see rsocket::net_io(const char*, size_t len)
296 *///=========================================================================
300 /// Length of format string (in bytes), or 0 to auto-detect length if format string is an ASCIIZ string
301 size_t len = 0) noexcept {
302 return rsocket().net_io(format, len, net_io().get()); // Call rsocket's net_io method on our internally-gathered group rsocket_io statistics
303 } // -x- std::string net_io -x-
306 /*======================================================================*//**
308 Send data to all rsocket endpoints that are in this rsocket_group.
310 This method is threadsafe.
311 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
312 containing successful operations that also record the number of
313 bytes that were transmitted, and the second one containing failed
314 operations that also record the exception that was thrown (see the
315 @ref rsocket_group_rc structure for details)
316 @see rsocket::__send(void*, size_t, int)
317 *///=========================================================================
318 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __send(
321 /// Number of bytes to send
323 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
324 const int flags = 0) noexcept {
326 // --------------------------------------------------------------------------
327 // Internal variables.
328 // --------------------------------------------------------------------------
329 std::vector<rsocket_group_rc>* v1 = new std::vector<rsocket_group_rc>();
330 std::vector<rsocket_group_rc>* v2 = new std::vector<rsocket_group_rc>();
331 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>* pair = new std::pair(*v1, *v2);
333 // --------------------------------------------------------------------------
334 // Loop through all rsocket objects in the underlying std::set.
336 // We don't need a mutex lock here because the __send method is threadsafe.
337 // --------------------------------------------------------------------------
338 for (std::weak_ptr<rsocket> wp : __r) {
339 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
340 if (r->__debug) r->debug("rsocket_group::send(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
342 + ", " + std::to_string(len)
343 + ", " + std::to_string(flags)
346 v1->push_back({r.get(), r->__send(msg, len, flags), nullptr});
347 } catch (std::exception* e) {
348 v2->push_back({r.get(), 0, e});
350 } // -x- if r.lock() -x-
351 } // -x- foreach __r -x-
354 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __send -x-
356 /*======================================================================*//**
358 Send data that included a final EoL sequence to all rsocket endpoints that
359 are in this rsocket_group.
361 This method is threadsafe.
362 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
363 containing successful operations that also record the number of
364 bytes that were transmitted, and the second one containing failed
365 operations that also record the exception that was thrown (see the
366 @ref rsocket_group_rc structure for details)
367 @see rsocket::__sendline(void*, size_t, int)
368 *///=========================================================================
369 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __sendline(
372 /// Number of bytes to send
374 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
375 const int flags = 0) noexcept {
377 // --------------------------------------------------------------------------
378 // Internal variables.
379 // --------------------------------------------------------------------------
380 std::vector<rsocket_group_rc>* v1 = new std::vector<rsocket_group_rc>();
381 std::vector<rsocket_group_rc>* v2 = new std::vector<rsocket_group_rc>();
382 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>* pair = new std::pair(*v1, *v2);
384 // --------------------------------------------------------------------------
385 // Loop through all rsocket objects in the underlying std::set.
387 // We don't need a mutex lock here because the __sendline method is
389 // --------------------------------------------------------------------------
390 for (std::weak_ptr<rsocket> wp : __r) {
391 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
392 if (r->__debug) r->debug("rsocket_group::send(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
394 + ", " + std::to_string(len)
395 + ", " + std::to_string(flags)
398 v1->push_back({r.get(), r->__sendline(msg, len, flags), nullptr});
399 } catch (std::exception* e) {
400 v2->push_back({r.get(), 0, e});
402 } // -x- if r.lock() -x-
403 } // -x- foreach __r -x-
406 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __sendline -x-
408 /*======================================================================*//**
410 Send data that is the final EoL sequence to all rsocket endpoints that are in
413 This method is threadsafe.
414 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
415 containing successful operations that also record the number of
416 bytes that were transmitted, and the second one containing failed
417 operations that also record the exception that was thrown (see the
418 @ref rsocket_group_rc structure for details)
419 @see rsocket::__sendline(void*, size_t, int)
420 *///=========================================================================
421 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __send_eol(
422 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
423 const int flags = 0) noexcept {
425 // --------------------------------------------------------------------------
426 // Internal variables.
427 // --------------------------------------------------------------------------
428 std::vector<rsocket_group_rc>* v1 = new std::vector<rsocket_group_rc>();
429 std::vector<rsocket_group_rc>* v2 = new std::vector<rsocket_group_rc>();
430 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>* pair = new std::pair(*v1, *v2);
432 // --------------------------------------------------------------------------
433 // Loop through all rsocket objects in the underlying std::set.
435 // We don't need a mutex lock here because the __sendline method is
437 // --------------------------------------------------------------------------
438 for (std::weak_ptr<rsocket> wp : __r) {
439 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
440 if (r->__debug) r->debug("rsocket_group::send_eol(socket{0x" + randolf::rtools::to_hex(r->__socket_fd) + "}"
441 + ", " + std::to_string(flags)
444 v1->push_back({r.get(), r->__send_eol(flags), nullptr});
445 } catch (std::exception* e) {
446 v2->push_back({r.get(), 0, e});
448 } // -x- if r.lock() -x-
449 } // -x- foreach __r -x-
452 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& __send_eol -x-
455 /*======================================================================*//**
457 Send data in the form of a std::string to all rsocket endpoints that are in
460 This method is threadsafe.
461 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
462 containing successful operations that also record the number of
463 bytes that were transmitted, and the second one containing failed
464 operations that also record the exception that was thrown (see the
465 @ref rsocket_group_rc structure for details)
466 @see rsocket::send(const std::string, const int)
467 *///=========================================================================
468 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send(
470 const std::string& msg,
471 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
472 const int flags = 0) {
473 return __send(msg.c_str(), msg.length(), flags);
474 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send -x-
476 /*======================================================================*//**
478 Send data in the form of a std::vector<char> to all rsocket endpoints that
479 are in this rsocket_group.
481 This method is threadsafe.
482 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
483 containing successful operations that also record the number of
484 bytes that were transmitted, and the second one containing failed
485 operations that also record the exception that was thrown (see the
486 @ref rsocket_group_rc structure for details)
487 @see rsocket::send(const std::vector<char>, const int)
488 *///=========================================================================
489 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send(
491 const std::vector<char>& msg,
492 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
493 const int flags = 0) {
494 return __send(msg.data(), msg.size(), flags);
495 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send -x-
497 /*======================================================================*//**
499 Send data in the form of a C-string to all rsocket endpoints that are in this
502 This method is threadsafe.
503 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
504 containing successful operations that also record the number of
505 bytes that were transmitted, and the second one containing failed
506 operations that also record the exception that was thrown (see the
507 @ref rsocket_group_rc structure for details)
508 @see rsocket::send(const char*, const size_t, const int)
509 *///=========================================================================
510 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send(
511 /// Pointer to data to send
513 /// Number of bytes to send
515 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
516 const int flags = 0) {
517 return __send(msg, len, flags);
518 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send -x-
520 /*======================================================================*//**
522 Send data in the form of an ASCIIZ string to all rsocket endpoints that are
523 in this rsocket_group., including the terminating NULL character.
525 This method is threadsafe.
526 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
527 containing successful operations that also record the number of
528 bytes that were transmitted, and the second one containing failed
529 operations that also record the exception that was thrown (see the
530 @ref rsocket_group_rc structure for details)
531 @see sendz(const char*, const int) which doesn't transmit the terminating
533 @see rsocket::send_asciiz(const char*, const int)
534 *///=========================================================================
535 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_asciiz(
536 /// Pointer to data to send
538 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
539 const int flags = 0) {
540 return __send(msg, std::strlen(msg) + 1, flags);
541 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_asciiz -x-
543 /*======================================================================*//**
545 Send one byte of data to all rsocket endpoints that are in this
548 This method is threadsafe.
549 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
550 containing successful operations that also record the number of
551 bytes that were transmitted, and the second one containing failed
552 operations that also record the exception that was thrown (see the
553 @ref rsocket_group_rc structure for details)
554 @see rsocket::send_byte(const char, const int)
555 *///=========================================================================
556 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_byte(
559 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
560 const int flags = 0) {
561 return __send(&value, sizeof(value), flags);
562 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_byte -x-
564 /*======================================================================*//**
566 Send the EoL sequence to all rsocket endpoints that are in this
569 This method is threadsafe.
570 @returns The same rsocket object so as to facilitate stacking
571 *///=========================================================================
572 template <typename T> std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_eol(
573 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
574 const int flags = 0) {
575 return __send_eol(flags);
576 } // -x- template <typename T> std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_eol -x-
578 /*======================================================================*//**
580 Send one byte of data to all rsocket endpoints that are in this
583 This method is threadsafe.
584 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
585 containing successful operations that also record the number of
586 bytes that were transmitted, and the second one containing failed
587 operations that also record the exception that was thrown (see the
588 @ref rsocket_group_rc structure for details)
589 @see rsocket::send_struct(const T, const int)
590 *///=========================================================================
591 template <typename T> std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_struct(
594 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
595 const int flags = 0) {
596 return __send(&value, sizeof(value), flags);
597 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_struct -x-
599 /*======================================================================*//**
601 Send one 16-bit unsigned integer of data in LSB (little endian) order to all
602 rsocket endpoints that are in this rsocket_group.
604 This method is threadsafe.
605 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
606 containing successful operations that also record the number of
607 bytes that were transmitted, and the second one containing failed
608 operations that also record the exception that was thrown (see the
609 @ref rsocket_group_rc structure for details)
610 @see rsocket::send_uint16_lsb(const uint16_t, const int)
611 *///=========================================================================
612 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint16_lsb(
614 const uint16_t value,
615 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
616 const int flags = 0) {
617 uint16_t buf = !__endian_is_msb ? value : ntohs(value);
618 return __send(&buf, sizeof(buf), flags);
619 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint16_lsb -x-
621 /*======================================================================*//**
623 Send one 16-bit integer of data in MSB (big endian) order to all rsocket
624 endpoints that are in this rsocket_group.
626 This method is threadsafe.
627 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
628 containing successful operations that also record the number of
629 bytes that were transmitted, and the second one containing failed
630 operations that also record the exception that was thrown (see the
631 @ref rsocket_group_rc structure for details)
632 @see rsocket::send_uint16_msb(const uint16_t, const int)
633 *///=========================================================================
634 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint16_msb(
636 const uint16_t value,
637 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
638 const int flags = 0) {
639 int16_t buf = __endian_is_msb ? value : htons(value);
640 return __send(&buf, sizeof(buf), flags);
641 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_int16_msb -x-
643 /*======================================================================*//**
645 Send one 32-bit unsigned integer of data in LSB (little endian) order to all
646 rsocket endpoints that are in this rsocket_group.
648 This method is threadsafe.
649 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
650 containing successful operations that also record the number of
651 bytes that were transmitted, and the second one containing failed
652 operations that also record the exception that was thrown (see the
653 @ref rsocket_group_rc structure for details)
654 @see rsocket::send_uint32_lsb(const uint32_t, const int)
655 *///=========================================================================
656 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint32_lsb(
658 const uint32_t value,
659 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
660 const int flags = 0) {
661 uint32_t buf = !__endian_is_msb ? value : ntohl(value);
662 return __send(&buf, sizeof(buf), flags);
663 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint32_lsb -x-
665 /*======================================================================*//**
667 Send one 32-bit unsigned integer of data in MSB (big endian) order to all
668 rsocket endpoints that are in this rsocket_group.
670 This method is threadsafe.
671 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
672 containing successful operations that also record the number of
673 bytes that were transmitted, and the second one containing failed
674 operations that also record the exception that was thrown (see the
675 @ref rsocket_group_rc structure for details)
676 @see rsocket::send_uint32_msb(const uint32_t, const int)
677 *///=========================================================================
678 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint32_msb(
680 const uint32_t value,
681 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
682 const int flags = 0) {
683 uint32_t buf = __endian_is_msb ? value : htonl(value);
684 return __send(&buf, sizeof(buf), flags);
685 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint32_msb -x-
687 /*======================================================================*//**
689 Send one 64-bit unsigned integer of data in LSB (little endian) order to all
690 rsocket endpoints that are in this rsocket_group.
692 This method is threadsafe.
693 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
694 containing successful operations that also record the number of
695 bytes that were transmitted, and the second one containing failed
696 operations that also record the exception that was thrown (see the
697 @ref rsocket_group_rc structure for details)
698 @see rsocket::send_uint64_lsb(const uint64_t, const int)
699 *///=========================================================================
700 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint64_lsb(
702 const uint64_t value,
703 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
704 const int flags = 0) {
705 uint64_t buf = !__endian_is_msb ? value : ((((uint64_t)ntohl((value) & 0xffffffffUL)) << 32) | ntohl((uint32_t)((value) >> 32)));
706 return __send(&buf, sizeof(buf), flags);
707 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint64_lsb -x-
709 /*======================================================================*//**
711 Send one 64-bit unsigned integer of data in MSB (big endian) order to all
712 rsocket endpoints that are in this rsocket_group.
714 This method is threadsafe.
715 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
716 containing successful operations that also record the number of
717 bytes that were transmitted, and the second one containing failed
718 operations that also record the exception that was thrown (see the
719 @ref rsocket_group_rc structure for details)
720 @see rsocket::send_uint64_msb(const uint64_t, const int)
721 *///=========================================================================
722 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint64_msb(
724 const uint64_t value,
725 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
726 const int flags = 0) {
727 uint64_t buf = __endian_is_msb ? value : ((((uint64_t)htonl((value) & 0xffffffffUL)) << 32) | htonl((uint32_t)((value) >> 32)));
728 return __send(&buf, sizeof(buf), flags);
729 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& send_uint64_msb -x-
731 /*======================================================================*//**
733 Send data in the form of a std::string to all rsocket endpoints that are in
734 this rsocket_group, with an EoL sequence appended.
735 @returns The same rsocket object so as to facilitate stacking
737 This method is threadsafe.
738 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
739 containing successful operations that also record the number of
740 bytes that were transmitted, and the second one containing failed
741 operations that also record the exception that was thrown (see the
742 @ref rsocket_group_rc structure for details)
743 @see recvline(const size_t, const int)
744 @see rsocket::sendline(const std::string, const int)
745 *///=========================================================================
746 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& sendline(
748 const std::string& msg = std::string(),
749 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
750 const int flags = 0) {
751 return __sendline(msg.c_str(), msg.length(), flags);
752 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& sendline -x-
754 /*======================================================================*//**
756 Send data in the form of an ASCIIZ string to all rsocket endpoints that are
757 in this rsocket_group.. The terminating NULL character won't be transmitted.
759 This method is threadsafe.
760 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
761 containing successful operations that also record the number of
762 bytes that were transmitted, and the second one containing failed
763 operations that also record the exception that was thrown (see the
764 @ref rsocket_group_rc structure for details)
765 @see send_asciiz which also transmits the terminating NULL character
766 @see rsocket::sendz(const char*, const int)
767 *///=========================================================================
768 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& sendz(
769 /// Pointer to data to send
771 /// MSG_DONTROUTE@n MSG_DONTWAIT@n MSG_EOR@n MSG_NOSIGNAL@n MSG_OOB
772 const int flags = 0) {
773 return __send(msg, std::strlen(msg), flags);
774 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& sendz -x-
776 /*======================================================================*//**
778 Shut down the underlying sockets in all rsocket objects that are in this
779 rsocket_group, partially or fully.
781 SHUT_RD: Further receives will be disallowed
782 SHUT_WR: Further sends will be disallowed (this may cause actions
783 specific to the protocol family of the socket to occur)
784 SHUT_RDWR: Further sends and receives will be disallowed (default)
785 @returns A std::pair of std::vector<rsocket_group_rc> vectors, the first one
786 containing successful operations that also record the number of
787 bytes that were transmitted, and the second one containing failed
788 operations that also record the exception that was thrown (see the
789 @ref rsocket_group_rc structure for details)
790 @see rsocket::shutdown(const int)
791 *///=========================================================================
792 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& shutdown(
793 /// SHUT_RD@n SHUT_RW@n SHUT_RDWR
794 const int how = SHUT_RDWR) {
796 // --------------------------------------------------------------------------
797 // Internal variables.
798 // --------------------------------------------------------------------------
799 std::vector<rsocket_group_rc>* v1 = new std::vector<rsocket_group_rc>();
800 std::vector<rsocket_group_rc>* v2 = new std::vector<rsocket_group_rc>();
801 std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>* pair = new std::pair(*v1, *v2);
803 // --------------------------------------------------------------------------
804 // Loop through all rsocket objects in the underlying std::set.
806 // We don't need a mutex lock here because the shutdown method is threadsafe.
807 // --------------------------------------------------------------------------
808 for (std::weak_ptr<rsocket> wp : __r) {
809 if (auto r = wp.lock()) { // Weak pointer is still valid, so acquire a std::unique_ptr while we access r
812 v1->push_back({r.get(), 0, nullptr});
813 } catch (std::exception* e) {
814 v2->push_back({r.get(), 0, e});
816 } // -x- if r.lock() -x-
817 } // -x- foreach __r -x-
820 } // -x- std::pair<std::vector<rsocket_group_rc>, std::vector<rsocket_group_rc>>& shutdown -x-
822 /*======================================================================*//**
824 Find out how many rsocket entries are in this group.
827 This method is threadsafe.
828 @returns Number of rsocket objects in the underlying std::set
829 *///=========================================================================
830 size_t size() noexcept {
831 const std::lock_guard<std::mutex> lock(__mutex);
833 } // -x- size_t size -x-
835 }; // -x- class rsocket_group -x-
837}; // -x- namespace randolf -x-