NetworkAdapter
C++ posix sockets abstraction layer for TCP and UDP communication
Install / Use
/learn @embtom/NetworkAdapterREADME
networkadapter
This library provides simplified C++ posix sockets abstraction layer for TCP and UDP communication. It also contains a handy c++ endian converter, based on class member reflection.
The dependencies are:
| Lib | Link | | ------------- | ------------- | | utilsCpp | https://github.com/embtom/utilsCpp | | docopt | http://docopt.org | | googletest | https://github.com/google/googletest |
Cmake library import
The networkadapter library provides exported cmake targets and can be imported by another component. After the installation of the networkadapter library it can be added with follwing cmake commands:
# Check if the development package of the library is available and import it
find_package(networkadapter REQUIRED)
# link library to the networkadapter
target_link_libraries(usage1 PRIVATE EMBTOM::networkadapter)
Examples
Some Examples are available how to use this TCP Server client example is located at "networkadapter/examples" and more examples are available.
1. EXA_HostName
Lookup of all available Ip addresses by hostname
EXA_HostName --host tom-LIFEBOOK
IPv4 Addresses:
192.168.1.80
IPv6 Addresses:
xxx:xx:971b:a700:4cf2:a936:4a93:xxx
2. EXA_InterfaceLookup
Lookup of all Ip addresses ordered by network devices
EXA_HostName --host tom-LIFEBOOK
IPv4 Addresses:
192.168.1.80
IPv6 Addresses:
xxx:xx:971b:a700:4cf2:a936:4a93:xxx
3. EXA_Tcp
Example implementation of a TCP Client and Server connection
EXA_TcpServer
Wait for connection
Connected by: 127.0.0.1
info Hallo
data1 ffaa0000
data2 aa00
disconnect 0
EXA_TcpServer
----
info Hallo
data1 ffaa0009
data2 aa09
disconnect 1
----
Disconntected
EXA_TcpClient -c localhost
try to connect to: localhost
info HALLOEcho
data1 ffaa0000
data2 aa00
disconnect 0
----
info HALLOEcho
data1 ffaa0009
data2 aa09
disconnect 1
----
finish
3. EXA_Udp
Example implementation of a UDP Client and Server connection
EXA_UdpServer
Connected from: 127.0.0.1
info Hallo
data1 ffaa0000
data2 aa00
disconnect 0
EXA_UdpClient -c localhost
try to connect to: localhost
DefaultRecive
info HALLOEcho
data1 ffaa0000
data2 aa00
disconnect 0
4. EXA_UdpBroadcast
Example implementation of a UDP broadcast reciver and transmitter. In this example the transmitter sends a broadcast message with a specified broadcast address into the network and the reciver response to the origin transmitter Ip.
EXA_BroadServer
Connected from: 192.168.1.22
info Hallo
data1 ffaa0000
data2 aa00
disconnect 0
EXA_Broadclient
Broadcast Address to send is: 192.168.1.255
DefaultRecive
info HALLOEcho
data1 ffaa0000
data2 aa00
disconnect 0
3. Detailed consideration based on example EXA_Tcp
The EXA_Tcp uses the networkadapter integrated endian converter. It is responsible to convert the protocol payload form host-byte-order to network-byte-order or vice versa. (networkadapter/example/Proto).
# Check if the development package is available and then import it
find_package(endianconversion REQUIRED)
add_library(ComProto INTERFACE)
target_include_directories (ComProto
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
# link library to the networkadapter integraded endianconverter
target_link_libraries(ComProto INTERFACE
EMBTOM::endianconversion
)
#include <NetOrder.h>
#include <HostOrder.h>
// Declaration of structure used as protocol info
struct ComProto
{
char info [10];
uint32_t data1;
uint16_t data2;
uint32_t disconnect;
};
// template for class members registrations and is specialized for ComProto protocol structure.
// Used for endian conversion by reflection
template <>
inline auto EtEndian::registerMembers<ComProto>()
{
return members(
member("info", &ComProto::info),
member("data1", &ComProto::data1),
member("data2", &ComProto::data2),
member("disconnect", &ComProto::disconnect)
);
}
The Tcp Server can look:
#include <iostream>
#include <iomanip>
#include <array>
#include <span.h>
#include <BaseSocket.hpp>
#include <Tcp/TcpServer.hpp>
#include <Tcp/TcpDataLink.hpp>
#include <ComProto.hpp>
constexpr int PORT_NUM = 5001;
using namespace EtNet;
int main(int argc, char *argv[])
{
// Creaton of a IPv4 Udp Socket and apply the socketoption SO_REUSEADDR
auto baseSocket = CBaseSocket::SoReuseSocket(
CBaseSocket(ESocketMode::INET_DGRAM));
CTcpServer CTcpServer(std::move(baseSocket), PORT_NUM);
CTcpDataLink DataLink;
CIpAddress peerIp;
while (true)
{
// Wait for connection of a TcpClient and returns the
// peerIp : Ip address ot the counterpart.
// DataLink: Object to transmit and recive with the Client established the connection
std::cout<< "Wait for connection" << std::endl;
std::tie(DataLink,peerIp) = CTcpServer.waitForConnection();
std::cout << "Connected by: " << peerIp.toString() << std::endl;
bool serverActive = true;
while(serverActive)
{
//Setup recive Buffer with endianconverter. (ComProto have to be registerd by "registerMembers")
EtEndian::CHostOrder<ComProto> rx;
DataLink.recive(rx);
// Convert recived data form Network-byte-order to Host-byte-order
const ComProto& rxData = rx.HostOrder();
// The same mechanism used at endian conversion could be used to plot the content of data
EtEndian::doForAllMembers<ComProto>(
[&rxData](const auto& member)
{
using MemberT = EtEndian::get_member_type<decltype(member)>;
std::cout << std::hex << " " << std::left
<< std::setw(20) << member.getName()
<< std::setw(20) << member.getConstRef(rxData)
<< std::endl;
});
std::cout << "----" << std::endl;
//Modify recived data and transmit it again
ComProto txData;
int i;
for(i = 0; i < strlen(rxData.info); i++) {
txData.info[i] = toupper(rxData.info[i]);
}
memcpy(&txData.info[i],"Echo",4);
txData.data1 = rxData.data1;
txData.data2 = rxData.data2;
txData.disconnect = rxData.disconnect;
if(rxData.disconnect) {
std::cout << "Disconntected" << std::endl;
serverActive = false;
}
// The convert the date to network-byte order again and transmit it.
DataLink.send(EtEndian::CNetOrder(txData));
}
}
}
Tcp Client
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <array>
#include <string>
#include <map>
#include <NetOrder.h>
#include <HostOrder.h>
#include <docopt.h>
#include <span.h>
#include <threadLoop.h>
#include <BaseSocket.hpp>
#include <Tcp/TcpClient.hpp>
#include <Tcp/TcpDataLink.hpp>
#include <ComProto.hpp>
constexpr int PORT_NUM = 5001;
using namespace EtNet;
using namespace EtEndian;
//*****************************************************************************
//! \brief EXA_TcpClient
//!
int main(int argc, char *argv[])
{
// docopt is used to describe the command line interface
constexpr std::string_view docOptCmd =
R"(EXA_TcpClient.
Usage:
EXA_TcpClient (-c | --connect) <hostname>
EXA_TcpClient (-h | --help)
EXA_TcpClient --version
Options:
-h --help Show this screen.
--version Show version.
)";
constexpr auto networkAdapterVersion = "networkAdapter " NETWORKING_ADAPTER_VERSION;
using ArgMap_t = std::map<std::string, docopt::value>;
ArgMap_t args = docopt::docopt(std::string(docOptCmd),
{ argv + 1, argv + argc },
true,
networkAdapterVersion);
if (!(args["<hostname>"])) {
return 0;
}
// Creaton of a IPv4 Udp Socket and apply the socketoption SO_REUSEADDR
auto baseSocket = CBaseSocket::SoReuseSocket(
CBaseSocket(ESocketMode::INET_DGRAM));
CTcpClient TcpClient(std::move(baseSocket));
std::string hostName(args["<hostname>"].asString());
std::cout << "try to connect to: " << hostName << std::endl;
// try to connect to conect to a server with hostname and port
EtNet::CTcpDataLink a;
try {
a = TcpClient.connect(hostName, PORT_NUM);
}
catch(const std::exception& e) {
std::cout << "Failed to establish connection: " << e.what() << std::endl;
return EXIT_FAILURE;
}
//setup of the threaded recive loop
unsigned rcvCount {0};
auto rcvFunc = [&a, &rcvCount]()
{
EtEndian::CHostOrder<ComProto> rx;
// the recive routine is blocking if no new data is available and with
// the assistance of "unblockRecive" the recive routine can unblock the
// thread loop can be prepared for joining.
if(CTcpDataLink::ERet::UNBLOCK == a.recive(rx))
{
std::cout << "finish" << std::endl;
return true;
}
// Convert recived data form Network-byte-order to Host-byte-order
const ComProto& rxData = rx.HostOrder();
// The same mechanism used at endian
