Archive
Spike To Learn
As one of the responsibilities on my last project, I had to interface one of our radars to a 10+ year old, legacy, command-and-control system built by another company. My C++11 code was required to receive commands from the control system and send radar data to it via UDP datagrams over a LAN that connects the two systems together.
It’s unfortunate, but we won’t get a standard C++ networking library until the next version of the C++ standard gets ratified in 2017. However, instead of using the low-level C sockets API to implement the interface functionality, I chose to use the facilities of the Poco::Net library.
Poco is a portable, well written, and nicely documented set of multi-function, open source C++ libraries. Among other nice, higher level, TCP/IP and http networking functionality, Poco::Net provides a thin wrapper layer around the native OS C-language sockets API.
Since I had never used the Poco::Net API before, I decided to spike off the main trail and write a little test program to learn the API before integrating the API calls directly into my production code. I use the “Spike To Learn” best practice whenever I can.
Here is the finished and prettied-up spike program for your viewing pleasure:
#include <string> #include <iostream> #include "Poco/Net/SocketAddress.h" #include "Poco/Net/DatagramSocket.h" using Poco::Net::SocketAddress; using Poco::Net::DatagramSocket; int main() { //simulate a UDP legacy app bound to port 15001 SocketAddress legacyNodeAddr{"localhost", 15001}; DatagramSocket legacyApp{legacyNodeAddr}; //create & bind //simulate my UDP app bound to port 15002 SocketAddress myAddr{"localhost", 15002}; DatagramSocket myApp{myAddr}; //create & bind //myApp creates & transmits a message //encapsulated in a UDP datagram to the legacyApp char myAppTxBuff[]{"Hello legacyApp"}; auto msgSize = sizeof(myAppTxBuff); myApp.sendTo(myAppTxBuff, msgSize, legacyNodeAddr); //legacyApp receives a message //from myApp and prints its payload //to the console char legacyAppRxBuff[msgSize]; legacyApp.receiveBytes(legacyAppRxBuff, msgSize); std::cout << std::string{legacyAppRxBuff} << std::endl; //legacyApp creates & transmits a message //to myApp char legacyAppTxBuff[]{"Hello myApp!"}; msgSize = sizeof(legacyAppTxBuff); legacyApp.sendTo(legacyAppTxBuff, msgSize, myAddr); //myApp receives a message //from legacyApp and prints its payload //to the console char myAppRxBuff[msgSize]; myApp.receiveBytes(myAppRxBuff, msgSize); std::cout << std::string{myAppRxBuff} << std::endl; }
As you can see, I used the Poco::Net::SocketAddress and Poco::Net::DatagramSocket classes to simulate the bi-directional messaging between myApp and the legacyApp on one machine. The code first transmits the text message “Hello legacyApp!” from myApp to the legacyApp; and then it transmits the text message “Hello myApp!” from the legacyApp to myApp.
Lest you think the program doesn’t work 🙂 , here is the console output after the final compile & run cycle:
As a side note, notice that I used C++11 brace-initializers to uniformly initialize every object in the code – except for one: the msgSize object. I had to fallback and use the “=” form of initialization because “auto” does not yet play well with brace-initializers. For who knows why, the type of obj in a statement of the form “auto obj{5};” is deduced to be a std::initializer_list<T> instead of a simple int. I think this strange inconvenience will be fixed in C++17. It may have been fixed already in the recently ratified C++14 standard, but I don’t know for sure. Do you?