Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
PeriodicNotifier.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_UPnP_SSDP_Server_PeriodicNotifier_h_
5#define _Stroika_Frameworks_UPnP_SSDP_Server_PeriodicNotifier_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
10#include "Stroika/Foundation/Common/Common.h"
14
15#include "Stroika/Frameworks/UPnP/Device.h"
18#include "Stroika/Frameworks/UPnP/SSDP/Server/LocationProvider.h"
19
20/*
21 * \file
22 *
23 * \note Code-Status: <a href="Code-Status.md#Alpha">Alpha</a>
24 */
25
26namespace Stroika::Frameworks::UPnP::SSDP::Server {
27
28 using Traversal::Iterable;
29
30 /**
31 * A big part of SSDP server functionality is to send periodic notifications of the Device description.
32 * Most applications want BasicServer instead, which does this and also answers searches (@see SearchResponder).
33 *
34 * Instantiating the class starts the (background) notifications automatically, and they
35 * continue until the PeriodicNotifier object is destroyed. (so a smart pointer to one of these is typically kept around
36 * for the life of the application). Destroying it says ssdp:byebye, so control points drop the device at once - rather
37 * than when its announcements' max-age runs out.
38 *
39 * \par Example Usage
40 * \code
41 * Execution::IntervalTimer::Manager::Activator intervalTimerMgrActivator; // once, near the start of main ()
42 * ...
43 * // announce this device, as a UPnP root device - with where deviceWS serves its description
44 * SSDP::Advertisement rootDevice;
45 * rootDevice.fServer = SSDP::MakeServerHeaderValue ("MyProduct/1.0"sv);
46 * rootDevice.fTarget = SSDP::kTarget_UPNPRootDevice;
47 * rootDevice.fUSN = Characters::Format ("uuid:{}::{}"_f, deviceID, SSDP::kTarget_UPNPRootDevice);
48 * PeriodicNotifier notifier{Sequence<SSDP::Advertisement>{rootDevice}, LocationFromBindings (deviceWS.bindings ())};
49 * \endcode
50 *
51 * \note requires Execution::IntervalTimer::Manager::Activator intervalTimerMgrActivator
52 *
53 * \note - this behavior differs from Stroika 2.1, where you had to explicitly call Run ()
54 * \note - requirement to instantiate IntervalTimer::Manager::Activator before this new in Stroika v3
55 */
57 public:
58 /**
59 * How often the NOTIFYs go out, and how long each says it is good for (its CACHE-CONTROL max-age) - a listener
60 * forgets a device whose announcement is not renewed by then. So fRepeatInterval must be less than fMaxAge: the UPnP
61 * Device Architecture recommends under half of it, and fMaxAge at least 1800 seconds.
62 *
63 * fRepeatInterval is the longest wait between them: each comes a random time between half of it and all of it after the
64 * last (the UPnP Device Architecture's "randomly-distributed interval"), so devices do not keep announcing together.
65 */
67 Time::DurationSeconds fRepeatInterval{3 * 60.0s};
68 Time::DurationSeconds fMaxAge{kDefaultMaxAge};
69 };
70
71 public:
72 /**
73 */
74 struct Options {
75 FrequencyInfo fFrequencyInfo{}; ///< how often to send the NOTIFYs
76 IO::Network::InternetProtocol::IP::IPVersionSupport fIPVersion{IO::Network::InternetProtocol::IP::IPVersionSupport::eDEFAULT}; ///< which SSDP channels: IPv4 (239.255.255.250) and/or IPv6 (ff02::c)
77 uint8_t fMulticastTTL{kDefaultMulticastTTL}; ///< the TTL (IPv6: hop limit) of each NOTIFY: how many routers it may cross (@see kDefaultMulticastTTL)
78 InterfaceFilter fInterfaces{DefaultInterfaceFilter}; ///< which network interfaces to send them out of - asked each time (@see SSDP::InterfaceFilter)
79
80 /**
81 * When a network appears, send the NOTIFYs right away, rather than at the next cycle.
82 * It costs a LinkMonitor (all share one thread, waiting on the OS's address-change notifications) - and, only while
83 * a burst of changes settles and is acted on, a thread of its own. Where the OS cannot tell (e.g. some containers),
84 * it is just not done.
85 */
86 bool fFollowNetworkChanges{true};
87 };
88 static const Options kDefaultOptions;
89
90 public:
91 /**
92 * Makes its sockets on construction, then sends the advertisements - out of every network interface
93 * options.fInterfaces accepts, each with the LOCATION location gives for that interface - after a random wait of up to
94 * 100 ms (as the UPnP Device Architecture asks, so devices starting together do not all announce at once; and again when
95 * a network appears), then about every options.fFrequencyInfo.fRepeatInterval (@see FrequencyInfo), until this object is
96 * destroyed. The interfaces are listed afresh each time, so networks that come and go are picked up. The advertisements'
97 * own fLocation is not used.
98 *
99 * Each set of NOTIFYs goes out twice, 100 ms apart, as UDP loses packets (the UPnP Device Architecture says more than once)
100 * - the first set during construction, so making it up to 200 ms slower; and destruction too, for its ssdp:byebye.
101 *
102 * Its sockets are not bound - they only send - so NOTIFYs come from an ephemeral port, not SSDP's 1900.
103 *
104 * Errors doing sends are just logged with DbgTrace()
105 */
106 PeriodicNotifier (const Iterable<Advertisement>& advertisements, const LocationProvider& location, const Options& options = kDefaultOptions);
107 PeriodicNotifier (const PeriodicNotifier&) = delete;
108
109 public:
110 const PeriodicNotifier operator= (const PeriodicNotifier&) = delete;
111
112 public:
113 /**
114 * \brief Stops the NOTIFYs, then sends an ssdp:byebye for each advertisement, out of each interface its last NOTIFYs went
115 * out of - in as many sets as the NOTIFYs. (A SearchResponder answering for the same device should go first, so no
116 * answer follows the byebye.)
117 */
119
120 public:
121 /**
122 * \brief The network interfaces its last NOTIFYs went out of - as they were then (so with the addresses they had then).
123 * Safe to call from any thread.
124 */
126
127 private:
128 shared_ptr<Execution::Synchronized<IO::Network::InterfacesByID>> fNotifyingOn_; // shared with the timer's callback
129 function<void ()> fSayByebye_;
130 unique_ptr<Execution::IntervalTimer::Adder> fIntervalTimerAdder_;
131 optional<SSDP::Private_::NetworkChangeFollower> fNetworkChanges_; // last, so destroyed first: no NOTIFY while the rest goes away
132 };
133
134}
135
136/*
137 ********************************************************************************
138 ***************************** Implementation Details ***************************
139 ********************************************************************************
140 */
141#include "PeriodicNotifier.inl"
142
143#endif /*_Stroika_Frameworks_UPnP_SSDP_Server_PeriodicNotifier_h_*/
constexpr uint8_t kDefaultMulticastTTL
The TTL (IPv6: hop limit) multicast SSDP packets - NOTIFYs and M-SEARCHes - go out with,...
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
nonvirtual IO::Network::InterfacesByID GetNetworkInterfaces() const
The network interfaces its last NOTIFYs went out of - as they were then (so with the addresses they h...
~PeriodicNotifier()
Stops the NOTIFYs, then sends an ssdp:byebye for each advertisement, out of each interface its last N...
Containers::KeyedCollection< Interface, Interface::SystemIDType, Containers::KeyedCollection_DefaultTraits< Interface, Interface::SystemIDType, Interface::IDExtractor > > InterfacesByID
Interfaces seen at one moment (e.g. one SystemInterfacesMgr::GetAll ()), keyed by fInterfaceID.
Definition Interface.h:396