pub struct Service { /* private fields */ }sync only.Expand description
High-level reactor-style SSDP service using mio.
Use a Service to discover network resources using SSDP, or to advertise
network resources which your program provides. Or both.
The implementation integrates with the mio crate, which provides a
“reactor-style” I/O API suited for running several I/O operations in a
single thread. (SSDP, being relatively low-bandwidth and non-urgent,
is unlikely to require any more high-performance I/O solution.)
An alternative mechanism, crate::AsyncService, integrates directly
with tokio instead of mio.
The implementation requires two UDP sockets: one bound to the well-known SSDP port number (1900) which subscribes to the multicast group, and a second bound to a random port for sending unicast searches and receiving unicast replies. (It would be possible to get by with a single socket if cotton-ssdp knew it was the only SSDP implementation running on that IP address – but if there might be other implementations running, it needs its own search socket in order not to steal other applications’ packets.)
For that reason, two MIO tokens are required; these should be passed
to Service::new, which takes care of registering them with the MIO
poller. Likewise, the main polling loop can indicate readiness on
either token at any time, and the corresponding “*_ready” method on
Service – Service::multicast_ready or Service::search_ready
– should be called in response. All this can be seen in the
ssdp-search-mio
example,
from which the example code below is adapted.
§Example subscriber
This code starts a search for all SSDP resources on the local
network, from all network interfaces, and stores unique ones in a
HashMap. The map will be populated as the MIO polling loop runs.
let map = RefCell::new(HashMap::new());
ssdp.subscribe(
"ssdp:all",
Box::new(move |r| {
let mut m = map.borrow_mut();
if let Notification::Alive {
ref notification_type,
ref unique_service_name,
ref location,
} = r {
if !m.contains_key(unique_service_name) {
m.insert(unique_service_name.clone(), r.clone());
}
}
}),
);§Example advertiser
This code sets up an advertisement for a (fictitious) resource, ostensibly available over HTTP on port 3333. The actual advertisements will be sent (and any incoming searches replied to) as the MIO polling loop runs.
(The UPnP Device Architecture specifies exactly what to advertise in the case of a UPnP implementation; this simpler example is not in itself compliant with that document.)
let uuid = uuid::Uuid::new_v4();
ssdp.advertise(
uuid.to_string(),
cotton_ssdp::Advertisement {
notification_type: "test".to_string(),
location: "http://127.0.0.1:3333/test".to_string(),
},
);Notice that the URL in the location field uses the localhost IP
address. The Service itself takes care of rewriting that, on a
per-network-interface basis, to the IP address on which each SSDP
peer will be able to reach the host where the Service is
running. For instance, if your Ethernet IP address is 192.168.1.3,
and your wifi IP address is 10.0.4.7, anyone listening to SSDP on
Ethernet will see http://192.168.1.3:3333/test and anyone listening on
wifi will see http://10.0.4.7:3333/test. (For how this is done, see the use
of rewrite_host in Engine::on_data.)
§The polling loop
The actual MIO polling loop, mentioned above, should be written in the standard way common to all MIO applications. For instance, it might look like this:
loop {
poll.poll(&mut events, Some(ssdp.next_wakeup())).unwrap();
if ssdp.next_wakeup() == std::time::Duration::ZERO {
ssdp.wakeup();
}
for event in &events {
match event.token() {
SSDP_TOKEN1 => ssdp.multicast_ready(),
SSDP_TOKEN2 => ssdp.search_ready(),
// ... other tokens as required by the application ...
_ => (),
}
}
}Implementations§
Source§impl Service
impl Service
Sourcepub fn new(registry: &Registry, tokens: (Token, Token)) -> Result<Self, Error>
pub fn new(registry: &Registry, tokens: (Token, Token)) -> Result<Self, Error>
Create a new Service, including its two UDP sockets
And registers the sockets with the mio::Registry
§Errors
Can return a std::io::Error if any of the underlying socket
calls fail.
Sourcepub fn subscribe<A>(
&mut self,
notification_type: A,
callback: Box<dyn Fn(&Notification)>,
)
pub fn subscribe<A>( &mut self, notification_type: A, callback: Box<dyn Fn(&Notification)>, )
Subscribe to notifications about a particular service type
Or subscribe to “ssdp:all” for notifications about all service types.
This call also sends fresh search messages.
Sourcepub fn advertise<USN>(
&mut self,
unique_service_name: USN,
advertisement: Advertisement,
)
pub fn advertise<USN>( &mut self, unique_service_name: USN, advertisement: Advertisement, )
Advertise a local resource on the network
Sourcepub fn deadvertise(&mut self, unique_service_name: &str)
pub fn deadvertise(&mut self, unique_service_name: &str)
Withdraw an advertisement for a local resource
For instance, it is “polite” to call this if shutting down cleanly.
Sourcepub fn multicast_ready(&mut self)
pub fn multicast_ready(&mut self)
Handler to be called when multicast socket is readable
Sourcepub fn search_ready(&mut self)
pub fn search_ready(&mut self)
Handler to be called when search socket is readable
Sourcepub fn next_wakeup(&self) -> Duration
pub fn next_wakeup(&self) -> Duration
Time before next wakeup