CHIRP#

Notes on Sockets#

CHIRP uses multicast for network discovery, meaning message are distributed to all computers in a network which subscribed to the corresponding multicast group. A multicast group is just a specific multicast IP address. CHIRP uses 239.192.7.123 as its multicast address and uses port 7123.

To receive multicasts, a socket is required which binds to the multicast endpoint (which is the multicast address and port) and joins the multicast group on each network interface. Since there might be multiple programs running CHIRP on one machine, it is important to ensure that the port is not blocked by one program. To ensure that a socket can be used by more than one program, the SO_REUSEADDR socket option has to be enabled. To receive multicast messages from other programs running CHIRP the IP_MULTICAST_LOOP socket option has to be enabled.

For sending, a separate socket for each interface is required. Setting the interface is done by using the IP_MULTICAST_IF option. Sending sockets also requires the SO_REUSEADDR socket option. Note that the IP_MULTICAST_LOOP option should not be set since it is better to add the loopback interface explicitly to avoid message duplication when using multiple network interfaces. Further it is not required to join the multicast group, sending can be achieved by sending a message to the multicast endpoint.

Multicasts can also “hop” over network boundaries (i.e. routers) using the IP_MULTICAST_TTL socket option (which defines the number of network hops). By default CHIRP uses a TTL of eight. However, routers might still be configured to drop multicast instead of forwarding them.

CHIRP Testing Tool#

To run the CHIRP testing tool, run:

./build/cxx/constellation/tools/chirp_manager CONSTELLATION_GROUP NAME INTERFACE

The following commands are available:

  • list_registered_services: list of services registered by the user in the manager

  • list_discovered_services [SERVICE]: list of services discovered by the manager and are in the same group

  • register_service [SERVICE] [PORT]: register a service in the manager

  • register_callback [SERVICE]: register a discover callback for a service that prints the discovered service

  • request [SERVICE]: send a CHIRP request for a given service

  • unregister_service [SERVICE] [PORT]: unregister a service in the manager

  • unregister_callback [SERVICE]: unregister a discover callback for a service

  • reset: unregister all services and callbacks, and forget discovered services

  • quit

constellation::chirp Namespace#

enum class constellation::chirp::ServiceStatus : std::uint8_t#

Status of a service for callbacks from the Manager

Values:

enumerator DISCOVERED#

The service is newly discovered

enumerator DEPARTED#

The service departed

enumerator DEAD#

The service is considered dead without departure

using constellation::chirp::DiscoverCallback = void(DiscoveredService service, ServiceStatus status, std::any user_data)#

Function signature for user callback

The first argument (service) contains the discovered service, the second argument is an enum that indicates the status of the service, i.e. whether it is newly discovered, departing, or considered dead, and the third argument (user_data) is arbitrary user data passed to the callback (done via Manager::RegisterDiscoverCallback).

It is recommended to pass the user data wrapped in an atomic std::shared_ptr since the callback is launched asynchronously in a detached std::thread. If the data is modified, it is recommended to use atomic types when possible or a std::mutex for locking to ensure thread-safe access.

struct DiscoverCallbackEntry#
#include <constellation/core/chirp/Manager.hpp>

Entry for a user callback in the Manager for newly discovered or departing services

Public Functions

std::strong_ordering operator<=>(const DiscoverCallbackEntry &other) const#

Public Members

DiscoverCallback *callback#

Function pointer to a callback

protocol::CHIRP::ServiceIdentifier service_id#

Service identifier of the service for which callbacks should be received

std::any user_data#

Arbitrary user data passed to the callback function

For information on lifetime and thread-safety see DiscoverCallback.

struct DiscoveredService#
#include <constellation/core/chirp/Manager.hpp>

A service discovered by the Manager

Public Functions

std::string to_uri() const#

Convert service information to a URI

std::strong_ordering operator<=>(const DiscoveredService &other) const#

Public Members

asio::ip::address_v4 address#

Address of the discovered service

message::MD5Hash host_id#

Host ID of the discovered service

protocol::CHIRP::ServiceIdentifier identifier#

Service identifier of the discovered service

networking::Port port#

Port of the discovered service

class Manager#
#include <constellation/core/chirp/Manager.hpp>

Manager handling CHIRP messages

Public Functions

Manager(std::string_view group_name, std::string_view host_name, const std::vector<networking::Interface> &interfaces)#
Parameters:
  • group_name – Group name of the group to join

  • host_name – Host name for outgoing messages

  • interfaces – Interfaces to use

virtual ~Manager()#
inline message::MD5Hash getGroupID() const#
Returns:

Group ID (MD5Hash of the group name)

inline message::MD5Hash getHostID() const#
Returns:

Host ID (MD5Hash of the host name)

void start()#

Start the background thread of the manager

bool registerService(protocol::CHIRP::ServiceIdentifier service_id, networking::Port port)#

Register a service offered by the host in the manager

Calling this function sends a CHIRP message with OFFER type, and registers the service such that the manager responds to CHIRP messages with REQUEST type and the corresponding service identifier.

Parameters:
  • service_id – Service identifier of the offered service

  • port – Port of the offered service

Return values:
  • true – if the service was registered

  • false – if the service was already registered

bool unregisterService(protocol::CHIRP::ServiceIdentifier service_id, networking::Port port)#

Unregister a previously registered service offered by the host in the manager

Calling this function sends a CHIRP message with DEPART type and removes the service from manager. See also RegisterService.

Parameters:
  • service_id – Service identifier of the previously offered service

  • port – Port of the previously offered service

Return values:
  • true – If the service was unregistered

  • false – If the service was never registered

void unregisterServices()#

Unregisters all offered services registered in the manager

Equivalent to calling UnregisterService for every registered service.

std::set<RegisteredService> getRegisteredServices()#

Get the list of services currently registered in the manager

Returns:

Set with all currently registered services

bool registerDiscoverCallback(DiscoverCallback *callback, protocol::CHIRP::ServiceIdentifier service_id, std::any user_data)#

Register a user callback for newly discovered or departing services

Note that a callback function can be registered multiple times for different services.

Warning

Discover callbacks block the execution of further processing of CHIRP requests and offers, callbacks that take a long time should offload the work to a new thread.

Parameters:
  • callback – Function pointer to a callback

  • service_id – Service identifier of the service for which callbacks should be received

  • user_data – Arbitrary user data passed to the callback function (see DiscoverCallback)

Return values:
  • true – If the callback/service/user_data combination was registered

  • false – If the callback/service/user_data combination was already registered

bool unregisterDiscoverCallback(DiscoverCallback *callback, protocol::CHIRP::ServiceIdentifier service_id)#

Unregister a previously registered callback for newly discovered or departing services

Parameters:
  • callback – Function pointer to the callback of registered callback entry

  • service_id – Service identifier of registered callback entry

Return values:
  • true – If the callback entry was unregistered

  • false – If the callback entry was never registered

void unregisterDiscoverCallbacks()#

Unregisters all discovery callbacks registered in the manager

Equivalent to calling UnregisterDiscoverCallback for every discovery callback.

void forgetDiscoveredService(protocol::CHIRP::ServiceIdentifier identifier, message::MD5Hash host_id)#

Forget the previously discovered services of the given type and host ID, if present.

Parameters:
  • identifier – Identifier of the discovered service to be forgotten

  • host_id – Host ID of the discovered service to be forgotten

void forgetDiscoveredServices(message::MD5Hash host_id)#

Forget all previously discovered services of a given host.

Parameters:

host_id – Host ID of the discovered services to be forgotten

void forgetDiscoveredServices()#

Forget all previously discovered services

std::vector<DiscoveredService> getDiscoveredServices()#

Returns list of all discovered services

Returns:

Vector with all discovered services

std::vector<DiscoveredService> getDiscoveredServices(protocol::CHIRP::ServiceIdentifier service_id)#

Returns list of all discovered services with a given service identifier

Parameters:

service_id – Service identifier for discovered services that should be listed

Returns:

Vector with all discovered services with the given service identifier

void sendRequest(protocol::CHIRP::ServiceIdentifier service_id)#

Send a discovery request for a specific service identifier

This sends a CHIRP message with a REQUEST type and a given service identifier. Other hosts might reply with a CHIRP message with OFFER type for the given service identifier. These can be retrieved either by registering a user callback (see RegisterDiscoverCallback) or by getting the list of discovered services shortly after (see GetDiscoveredServices).

Parameters:

service_id – Service identifier to send a request for

struct MulticastMessage#
#include <constellation/core/chirp/MulticastSocket.hpp>

Incoming multicast message

Public Members

std::vector<std::byte> content#

Content of the message in bytes

asio::ip::address_v4 address#

Address from which the message was received

class MulticastSocket#
#include <constellation/core/chirp/MulticastSocket.hpp>

Multicast handler for multicast message

Public Functions

MulticastSocket(const std::vector<networking::Interface> &interfaces, const asio::ip::address_v4 &multicast_address, asio::ip::port_type multicast_port)#

Construct multicast handler

Parameters:
  • interfaces – List of interfaces for outgoing messages

  • multicast_address – Multicast address

  • multicast_port – Multicast port

void sendMessage(std::span<const std::byte> message)#

Send multicast message to all interfaces

Parameters:

message – Message in bytes

std::optional<MulticastMessage> recvMessage(std::chrono::steady_clock::duration timeout)#

Receive multicast message within a timeout

Parameters:

timeout – Duration for which to block function call

Returns:

Multicast message if received

struct RegisteredService#
#include <constellation/core/chirp/Manager.hpp>

A service offered by the host and announced by the Manager

Public Functions

std::strong_ordering operator<=>(const RegisteredService &other) const#

Public Members

protocol::CHIRP::ServiceIdentifier identifier#

Service identifier of the offered service

networking::Port port#

Port of the offered service