Fire-And-Forget wrapper for sending simple UDP data using boost::asio libraries

So I had a problem, as part of an embedded software system I was working on I needed to periodically send some GPS information via UDP datagrams to other devices on the network – really simple stuff, transmit a string to an IP address on a given port, Fire And Forget, send a string from here to there – end of story!

Now my normal port of call these days for networking in C++ is to reach for the boost::asio libs, as mentioned here, this library is very powerful and flexible and can handle a huge number of different networking scenarios.

All well and good, but sometimes I wish that I could just call a simple function and not have to remember or worry about all of boost stuff as it can be a proper head-wreck and there’s a lot of typing as the namespaces are so long! Python has me really spoiled, it makes so many things simple and easy to use!

So I wrote a little C++ wrapper class that just has 1 function called send() that can send a string or the contents of a binary buffer, the class is called: boost_udp_send_faf (faf stands for Fire-and-Forget!).

It is a very basic wrapper around the boost libs and only handles very simple transmission use-cases, but these use-cases probably cover about 80% of my UDP transmission needs!

To use the class for a single Fire-and-Forget send:

//
#include "boost_udp_send_faf.h"

boost_udp_send_faf("192.168.1.44", 8861).send("The message!");
//

This sends the message to 192.168.1.44 on port 8861.

If your program needs send multiple datagrams, then we can keep the socket open and reuse the end-point like this:

//
#include "boost_udp_send_faf.h"

boost_udp_send_faf sender("192.168.1.44", 8861);
	
for (auto i = 0; i != 10; i++) {
    sender.send("Lots of messages! :-/");
}
//

The socket will remain open until the ‘sender’ object goes out of scope.

It’s probably best to add exception handling to your calls as they can fail for lots of reasons and as ever networking is very flaky & unpredictable.

//
try {
    boost_udp_send_faf("192.168.1.44", 8861).send("The message!");
}
catch (const boost::system::system_error& ex) {
    cerr << "Send failed: " << ex.what();
}
//

This will also ensure that the socket is closed once the message is sent as the object will go out of scope when execution leaves the try block.

The code for boost_udp_send_faf can be found in this repo, but I have in-lined it below as well:

#pragma once

//   Copyright 2018 Kevin Godden
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

#include 
#include 

//
// Fire-and-Forget (FAF) transmission for UDP
// using the boost::asio lib. Very simple wrapper
// around the boost libs.
//
// Can throw the following exception:
//	boost::system::system_error
//
// Synopsis --->
//
// For a single Fire-and-Forget send:
//
// #include "boost_udp_send_faf.h"
//
// boost_udp_send_faf("192.168.1.44", 8861).send("The message!");
//
//
// To send multiple datagrams while keeping the socket open and
// reusing end-point:
//
// #include "boost_udp_send_faf.h"
//
// boost_udp_send_faf sender("192.168.1.44", 8861);
//		
// for (auto i = 0; i != 10; i++) {
//     sender.send("Lots of messages! :-/");
// }
//
// The socket will remain open while the boost_udp_send_faf object is in scope
// so to control when the socket is closed, control the scope of the object.
//
// If you want to do anything fancy like using scatter-gather buffers etc. then
// just use the boost libs!  This is for real simple stuff!
//


class boost_udp_send_faf {
	boost::asio::io_service io_service;
	boost::asio::ip::udp::socket socket;
	boost::asio::ip::udp::endpoint remote_endpoint;

public:

	boost_udp_send_faf(const std::string& ip_address, const int port, const bool broadcast = false) : socket(io_service) {
		
		// Open socket
		socket.open(boost::asio::ip::udp::v4());

		// I wouldn't recommend broadcasting unless you are
		// in complete control of your subnet and know
		// what's on it and how it will react
		if (broadcast) {
			boost::asio::socket_base::broadcast option(true);
			socket.set_option(option);
		}

		// make endpoint
		remote_endpoint = boost::asio::ip::udp::endpoint(boost::asio::ip::make_address(ip_address.c_str()), port);
	}

	// Send a string to the preconfigured endpoint
	// via the open socket.
	void send(const std::string& message) {
		boost::system::error_code ignored_error;
		socket.send_to(boost::asio::buffer(message), remote_endpoint, 0, ignored_error);
	}

	// Send some binary data to the preconfigured endpoint
	// via the open socket.
	void send(const unsigned char* data, const int len) {
		boost::system::error_code ignored_error;
		socket.send_to(boost::asio::buffer(data, len), remote_endpoint, 0, ignored_error);
	}
};