libmcrx is a low-level library for receiving multicast UDP traffic.
The code for receiving multicast traffic is different on different platforms, and has some quirks and complexities. This library is intended to ease adoption of multicast-based protocols by simplifying the programming task of receiving multicast packets at the client side. Its API uses C linkage in an attempt to maximize the portability for implementing simple wrappers in other languages.
The library also is intended to serve as an extension point to integrate with some standards and standard-tracked work in progress in the IETF, ideally including:
For normal linux builds of the library, this project uses autoconf and automake, following the libabc template.
apt-get install \
autoconf \
libtool-bin \
make \
build-essential
Using brew:
brew install \
autoconf \
automake \
make \
libtool
TBD: more platforms
./autogen.sh
./configure
make
As usual with autotools, ./configure --help
provides a bunch of options, and more in-depth explanations give more useful details.
For example, with something like --prefix=${HOME}/local-install
a make install
will not need sudo, and will put the library and header files under ${HOME}/local-install.
Note that to test this library, you'll need reachability to an active sender of multicast traffic. See the how-to for some approaches.
NB: the current tests use a hardcoded (S,G), and probably should be changed to use a config file instead. As-is you'll basically need to be using the multicast-ingest-platform or to be in a multicast-capable network that performs ingest using DRIAD for it to pass, assuming the sender it uses is running.
make check
Autoconf by default should build a configure and Makefiles that will put the headers and libraries into the default location for user libraries for the system.
sudo make install
This library is structured as a few types of objects in a hierarchy, wrapping as much socket-receiving complexity as we could arrange.
The relevant objects are:
Each of these objects is created with a "_new" function, and is destroyed with a "_unref" function. (An internal refcount may be increased with a "_ref" function, which will keep the object alive through one extra "_unref".)
Each object also can hold an arbitrary "user data" pointer, set and retrieved with a set_userdata and get_userdata function. That pointer is opaque to the library, and provided as a convenience for the calling system.
Basic usage looks like this:
#include <mcrx/libmcrx.h>
static int receive_cb(struct mcrx_packet* packet);
int receive_thread() {
struct mcrx_ctx* ctx = NULL;
mcrx_ctx_new(&ctx);
struct mcrx_subscription_config conf = MCRX_SUBSCRIPTION_CONFIG_INIT;
mcrx_subscription_config_pton(&conf, "23.212.185.4", "232.1.1.1");
conf.port = 5001;
struct mcrx_subscription *sub;
mcrx_subscription_new(ctx, &conf, &sub);
mcrx_subscription_set_receive_cb(sub, receive_cb);
mcrx_subscription_join(sub);
while (1) {
mcrx_ctx_receive_packets(ctx);
}
}
static int receive_cb(struct mcrx_packet* packet) {
// do something with packet
// operations like creating, joining, and leaving subscriptions are
// safe here.
mcrx_packet_unref(packet);
return MCRX_RECEIVE_CONTINUE;
}
For a more detailed example including appropriate error handling, please see mcrx-check.
There is no thread safety handling inside the library. It's the caller's responsibility to ensure that no calls to any functions using the same ctx or the objects generated from the same ctx (including packets and subscriptions) have function calls that overlap in time between different threads.
For an example integrating with an external event handler instead of using the blocking mcrx_ctx_receive_packets call, see the libmcrx integration with python-asyncio-taps.
That project uses python's asyncio as the event handling library, and exports sockets to be added to the list of sockets to monitor for read readiness via mcrx_ctx_set_receive_socket_handlers, rather than using the blocking mcrx_ctx_receive_packets call.
In that scenario, the calling system is responsible for making a timely call to the do_receive function for all the sockets that have been given to the calling system with the add_socket_cb callback and that have not yet been removed with the remove_socket_cb callback.