/**
* @mainpage
* @anchor mainpage
* @brief
* @details
* @copyright Russell John Childs, PhD, 2015
* @author Russell John Childs, PhD
* @date 2015-06-21
*
* This file contains classes: Stream, Message, Socket, Verify.
*
* e Stream e: Wraps a char* buffer so that "<<" and ">>" can be used.
*
* e Message e: Serialises port, ip, message string to and from wrapped char buf[]
* using "<<" and ">>" operators. This makes application less code intensive
* when transferring data between application and socket
*
* e Socket e: A generic, multithreaded, multi-socket class that is used
* to create a SOCK_STREAM, SOCK_DGRAM or multicast server or client
*
* e Verify e: A class for recording passes/fails of expected==actual.
*
* Compiled and tested under Linux Mint, using g++ 4.8.
*
* g++ options: -O0 -g3 -Wall -O0 -fopenmp -mavx -m64 -g -Wall -c
* -fmessage-length=0 -fno-omit-frame-pointer --fast-math
* -std=c++11 -I/opt/intel/vtune_amplifier_xe_2013/include/
*
* Linker options: -Wl,--no-as-needed -fopenmp
* -L/opt/intel/vtune_amplifier_xe_2013/lib64/
*
* Cmd line options: -lpthread -latomic -littnotify -ldl
*
* Documentation: Doxygen comments for interfaces, normal for impl.
*
* @file sockets.cpp
* @see
* @ref mainpage
*/
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <queue>
#include <string>
#include <sstream>
#include<functional>
#include <future>
#include <mutex>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cxxabi.h>
#include <string.h>
/**
* addtogroup Sockets
* @{
*/
namespace Sockets
{
enum ApplicationType
{
server = 0, /**< Creates a server */
client = 1 /**< Creates a client */
};
/**
* This class wraps a buffer, char buf[Size], so that it may be used with
* iostream "<<" and ">>"
*/
struct Stream : public std::streambuf
{
/**
* Sets range of put and get pointers in buf[]
* tparam Size {int} - deduced size of wrapped buffer char buf[Size]
*/
template<int Size>
Stream( char (&buffer)[Size])
{
setg(buffer, buffer, buffer+Size);
setp(buffer, buffer+Size);
}
~Stream( void )
{
}
/**
* Override of virtual function.
* @see std::streambuf::underflow
*/
int_type underflow( void )
{
return gptr() == egptr() ?
traits_type::eof() :
traits_type::to_int_type(*gptr());
}
/**
* Override of virtual function.
* @see std::streambuf::overflow
*/
int_type overflow( void )
{
return pptr() == epptr() ?
traits_type::eof() :
traits_type::to_int_type(*epptr());
}
};
/**
* Serialises data between buffer[] and socket, via operators "<<" and ">>".n
* Usage example:n
* std::pair<const char*, int> buf;n
* std::string buf_1;
*
* message >> reference_wrapper<int>(descriptor) >> buf >>
* reference_wrapper<sockaddr_storage*>(addr); //order independent, vars optl
* n
* message_1 >> reference_wrapper<int>(descriptor) >> buf_1 >>
* reference_wrapper<sockaddr_storage*>(addr); //order independent, vars opt
*
* message_2 << buf << reference_wrapper<int>(descriptor) <<
* reference_wrapper<sockaddr_storage*>(addr); //order independent, vars opt
* n
* message_3 << reference_wrapper<sockaddr_storage*>(addr) <<
* reference_wrapper<int>(descriptor) << buf_1; //order independent, vars opt
*/
struct Message
{
/**
* tparam Size {int} - size of wrapped buffer (NB: deduced)
* @param buffer {char (&)[Size]} - Reference to wrapped char buffer[]
* @param descriptor {int} - Socket descriptor
* @param address {sockaddr_storage*} - Client or Server address of socket
*/
template<int Size>
Message( char (&buffer)[Size], int descriptor=-1,
sockaddr_storage *address=0 ) :
m_buffer(buffer),
m_size(Size),
m_stream(buffer),
m_descriptor(descriptor),
m_address(address),
m_in(&m_stream),
m_out(&m_stream)
{
}
~Message( void )
{
}
/**
* Extractor that extracts values from wrapped buffer to "msg" argument.
* tparam T - type of variable to be extracted to (deduced)
* @param msg {T} - variable to extract to
*
* Usage:
*
* my_message >> my_int >> my_float >> my_string;
*/
template< typename T>
Message& operator>>(T& msg)
{
m_in >> msg;
m_in.get();
return *this;
}
/**
* Extractor that copies wrapped buffer to "msg" argument.
* @param msg {std::string&} - variable to extract to.
*
* Notes: Preserves, rather than skips, whitespace.
*/
Message& operator>>(std::string& msg)
{
std::getline(m_in, msg);
m_in.get();
return *this;
}
/**
* Extractor that extracts socket descriptor.
* @param descriptor {std::reference_wrapper<int>} -
* variable to extract to
*
* Notes: std::reference_wrapper is used to disambiguate
* ">> int_from_client" and ">> reference_wrapper<int>(descriptor)"
*
*/
friend
Message& operator>>(Message& in, std::reference_wrapper<int> descriptor)
{
descriptor.get() = in.m_descriptor;
return in;
}
/**
* Extractor that extracts socket's client/server address.
* @param address {std::reference_wrapper<sockaddr_storage*>} -
* variable to extract to.
*
* The argument is a pointer, so that it can be used with UNIX sockets.
*/
friend
Message& operator>>(Message& in,
std::reference_wrapper<sockaddr_storage*> address)
{
address.get() = in.m_address;
return in;
}
/**
* Extractor that extracts wrapper buffer to char* msg.
* @param msg {const char*} - variable to extract to
*/
friend
Message& operator>>(Message& in, const char*& msg)
{
msg = in.m_buffer;
return in;
}
/**
* Extractor that extracts wrapper buffer & size.
* @param msg {std::pair<const char*, int>&} - variable to extract to
*/
friend
Message& operator>>(Message& in,
std::pair<const char*, int>& msg)
{
msg.first = in.m_buffer;
msg.second = in.m_size;
return in;
}
/**
* Inserter that inserts values into wrapped buffer to "msg" argument.
* tparam T type of variable to insert from
* @param msg {T} - variable to insert from
*
* Usage:
*
* my_message << my_int << my_float << my_string;
*/
template< typename T>
Message& operator<<(const T& msg)
{
m_out << msg << std::endl;
return *this;
}
/**
* Inserter that copies wrapper buffer & size.
* @param {std::pair<const char*, int>&} msg - variable to insert from
*/
friend
Message& operator<<(Message& in,
const std::pair<const char*, int>& msg)
{
in.m_buffer = msg.first;
in.m_size = msg.second;
return in;
}
private:
const char* m_buffer;
int m_size;
Stream m_stream;
int m_descriptor;
sockaddr_storage *m_address;
std::istream m_in;
std::ostream m_out;
};
/**
* Helper that stores destination port, IP, request string to initiate
* handshake and socket backlog
*/
struct PortIpRequestBacklog
{
/**
* @param port {const std::string&}
* @param ip {const std::string&}
* @param request {const std::string&}
* @param backlog {int}
*/
PortIpRequestBacklog( const std::string& port, const std::string& ip="",
const std::string& request="", int backlog=10) :
m_port(port),
m_ip(ip),
m_request(request),
m_backlog(backlog)
{
}
std::string m_port;
std::string m_ip;
std::string m_request;
int m_backlog;
};
/**
* Used with SocketInfo and Socket::operator[] to extract peer/host address
*/
enum PeerOrHost
{
peer = 0, /**< Signifies remote machine for socket */
host = 1, /**< Signifies local machine for socket */
undefined = 2 /**< Signifies that getpeername/getsockname not called */
};
/**
* Allows Socket<>::operator[] to be overloaded by return type.
*
* tparam T {PeerOrHost} - whether info is for peer, host or undefined
*
* Usage example:
* SocketInfo<host> info = my_socket[sock_descriptor]; n
* sockaddr_storage *host = info; n
* or n
* sockaddr_storage *host=(SocketInfo<peer>)my_socket[sock_descriptor];
*/
template< PeerOrHost T = undefined>
struct SocketInfo
{
SocketInfo( void ) :
m_descriptor(0)
{
memset(&m_addr, 0, sizeof(sockaddr_storage));
}
/**
* Initialises descriptor and address
* @param descriptor {int} - socket descriptor
*
* Notes: Zeroing sockaddr_storage seems cause getpeer(sock)name to work
* incorrectly.
*/
SocketInfo( int descriptor) :
m_descriptor( descriptor )
{
//memset(&m_addr, 0, sizeof(sockaddr_storage));
}
~SocketInfo( void )
{
}
/**
* Returns the remote machine (peer) address container. This mechanism
* requires less code than overloading via a covariant return type.
*/
operator SocketInfo<peer>&( void )
{
socklen_t len = sizeof(sockaddr_storage);
getpeername(m_descriptor,(sockaddr*)&m_addr, &len);
return *(SocketInfo<peer>*)this;
}
/**
* Returns the local machine (host) address container. This mechanism
* requires less code than overloading via a covariant return type.
*/
operator SocketInfo<host>&( void )
{
socklen_t len = sizeof(sockaddr_storage);
getsockname(m_descriptor,(sockaddr*)&m_addr, &len);
return *(SocketInfo<host>*)this;
}
/**
* Extracts address from address container structure.
*
*/
operator sockaddr_storage*( void )
{
return &m_addr;
}
//private:
const int m_descriptor;
sockaddr_storage m_addr;
};
/**
* This encapsulates the creation of a list of one or more sockets, of the
* same type: stream, datagram, multicast datagram, or connected datagram.
* The class may used to create a client or a server.
*
* Usage example:
* //Create a stream server n
* Socket<SOCK_STREAM, server> server_socket; n
* server_socket.create(vector<PortIpRequestBacklog>(port));
* server_socket.run(callback_func_for_server_recv); //Call within thread
*
* //Create a connected dgram client n
* Socket<SOCK_DGRAM, client> client_socket(true); //false/true is un/connected
* n //create(...), run(callback_func_for_client_recv) in thread
*
* //Create a multicast dgram server ('/' marks start of multicast ip) n
* Socket<SOCK_DGRAM, server> server_socket_1; n
* server_socket.create(vector<PortIpRequestBacklog>(port, "/225.0.0.37")); n
* //create(...), run(...) in thread
*
* //Create a multicast dgram client ('/' marks start of multicast ip) n
* Socket<SOCK_DGRAM, client> client_socket_1; n
* server_socket.create(vector<PortIpRequestBacklog>(dest_port,"/225.0.0.37"));
* n //create(...), run(...) in thread
*
* tparam SockType {int} SOCK_STREAM | DGRAM - type of socket
* tparam AppType {ApplicationType} server | client - App is client or server
* tparam Family {int} - internet family (see addrinfo)
* tparam BufferSize {int} - size of buffer used to receive/send TCP/UDP msg
*
* @see man getaddrinfo
*
* Notes:
*
* In this implementation, a server or client sends and receives on the
* same socket. This is to permit multithreading without locks. In a future
* implementation they will be able to send a single msg to multiple
* sockets.
*
* The current implementation allows a client to multicast to
* multiple servers on the same socket from which it receives a msg, but
* all servers must have the same port number.
*
* The user may effect sending of a single msg to multiple sockets, but
* must provide their own thread-safety locks and record which socket
* descriptor has been allocated to each server address.
*/
template< int SockType=SOCK_STREAM, ApplicationType AppType=client,
int Family=AF_UNSPEC, int BufferSize = 256>
class Socket
{
public:
/**
* @param connected {bool} - false/true is un/connected. Used with DGRAM.
* @param flags {int} - addrinfo flags
* @param prot {int} - internet protocol (see addrinfo)
* @see man getaddrinfo
*/
Socket( bool connected=false, int flags=0, int prot=0) :
m_quit(true),
m_error(""),
m_hints{flags, Family, SockType, prot},
m_max(0),
m_connected(connected)
{
FD_ZERO(&m_descriptors);
}
~Socket( void )
{
}
/**
* Checks if the app is running or has exited due to error. If no error has
* occurred it returns true. If quit() has been called on the app, then
* is_valid() will return true, unless include_quit is set, when it will
* return false.
*
* @param include_quit {bool} - If set, returns false if quit() was called
*/
bool is_valid( bool include_quit=true )
{
return !(include_quit && m_quit == true) && (m_error == "");
}
/**
* Creates the socket
*
* @param in {const PortIpRequestBacklog&} - server/dest port, IP, string to
* be sent from client to server to start handshake, backlog val for socket
*
* Notes: For a multicast the IP should begin with '/'. Eg: "/225.0.0.37".
*/
void create(const PortIpRequestBacklog& in)
{
//Extract port, IP, handshake string, backlog
const std::string& port = in.m_port;
const std::string& ips = in.m_ip;
int backlog = in.m_backlog;
const std::string& request = in.m_request;
//Extract multicast IP, if it exists
auto split = [](const std::string& in, std::string& a, std::string& b)
{
auto p = in.find("/");
a = p == std::string::npos ? in : in.substr(0,p);
b = p == std::string::npos ? "" : in.substr(p+1,in.size()-p-1);
};
std::string ip, ip_multicast;
split(ips, ip, ip_multicast);
ip = AppType==server || ip_multicast.empty()==true ? ip : ip_multicast;
//m_hints.ai_flags = ip.size()==0 ? AI_PASSIVE : 0;
m_hints.ai_flags =ip.size()==0 && AppType==server ? AI_PASSIVE : 0;
//Get addrinfo struct, set error if unsccessful
addrinfo *info;
const char *url = ip.empty() ? 0 : ip.data();
m_error= getaddrinfo(url, port.data(), &m_hints, &info) != 0 ?
"getaddrinfo":"";
//Loop over addresses while no error, addr not null
auto tmp = info;
int sock = -1;
while( is_valid( false ) && tmp != NULL && sock < 0 )
{
//Make socket
sock = socket(tmp->ai_family, tmp->ai_socktype,
tmp->ai_protocol);
m_error = sock < 0 ? "socket" : "";
if(is_valid( false ))
{
//Set socket options
setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &tmp,
sizeof(int) );
//Bind socket to port, if server
if(AppType == server)
{
m_error = bind(sock,tmp->ai_addr,tmp->ai_addrlen)>= 0 ?
"" : "bind";
}
//Connect to server if app is a client and stream/conn dgram
else if(SockType == SOCK_STREAM || m_connected)
{
m_error=connect(sock,tmp->ai_addr,tmp->ai_addrlen)>=0 ?
""
: "connect";
//Send handshake string to stream/conn dgram server
if(is_valid( false ) && request.empty()==false)
{
m_error =
send(sock, request.c_str(), request.size()+1, 0)>0?
"" : "send";
}
}
else
{
//Send handshake string to unconn dgram server
if(is_valid( false ) && request.empty()==false)
{
m_error =
sendto(sock, request.c_str(), request.size()+1, 0,
tmp->ai_addr, tmp->ai_addrlen) > 0 ? "" : "sendto";
}
}
//Close socket, set error if bind unsuccessful
if(is_valid( false ) == false)
{
close(sock);
sock = -1;
tmp = tmp->ai_next;
m_error = tmp == 0 ? "bind" : "";
}
//Subscribe to multicast group if "listener" (server)
else if( ip_multicast.empty() == false && AppType == server)
{
ip_mreq mreq;
mreq.imr_multiaddr.s_addr=inet_addr(ip_multicast.data());
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
m_error = setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq))>=0 ? "" :"multicast";
}
}
}
if( is_valid( false ) )
{
freeaddrinfo(info);
// If server and stream start listener, record descriptor,
// set up fd set and max descriptor val for select()
if( AppType == server && SockType == SOCK_STREAM)
{
m_error = listen(sock, backlog) == -1 ? "listen" : "";
if( is_valid( false ) )
{
m_listener_array.insert(sock);
FD_SET( sock, &m_descriptors );
m_max = sock;
}
}
//If client or dgram, record descriptor,
// set up fd set and max descriptor val for select()
else
{
m_descriptor_array.insert(sock);
FD_SET( sock, &m_descriptors );
m_max = sock;
}
}
}
/**
* Creates multiple sockets, each associated with its own port (if server)
* or port and dest address (if client)
*
* @param in {const std::vector<PortIpRequestBacklog>&} - list of port,
* <IP>, handshake string, backlog for each of the sockets to be created
*/
void create( const std::vector<PortIpRequestBacklog>& in)
{
for(auto&& i : in )
{
create(i);
}
}
enum DescriptorType
{
listeners = 0, /**< Descriptors will be for listener sockets */
descriptors = 1 /**< Descriptors will be for send/recv sockets */
};
/**
* Sets the socket(s) to be running, i.e. ready to accept connections and
* send/recv msgs
*
* tparam Functor {void(Message& message)} - Callback function to be executed upon recv/recvfrom
of msg.
* par
* The msg is passed to the callback as a Message&
* argument. The callback may extract the port, IP, msg string of sender
* and return a response via Message::operator<<(...),
* par
* eg message << some_string;
*/
template< typename Functor, unsigned ThreadCount=1, unsigned TimeOut=5 >
void run( Functor& callback)
{
m_quit = false;
//Keep track of num of threads, {descriptor, bytes_recv} returned as
//futures by std::async threads
unsigned thread_count = 0;
std::pair<int, int> descriptor_bytes_recv;
std::vector<std::future<std::pair<int, int>> > threads;
std::vector<std::pair<int, int>> desc_bytes;
std::unordered_set<int> in_use;
//Lambda that blocks if max thread count reached
auto wait_for_threads = [&]()
{
if( thread_count == ThreadCount )
{
//If max reached, wait for completion of threads
for( auto&& res : threads )
{
res.wait();
//store {descriptor, bytes_recvd} returned by thread
desc_bytes.push_back(res.get());
}
//Clear list of futures and thread count
threads.clear();
thread_count = 0;
}
};
while(is_valid())
{
//Block until a socket is ready for reading
auto readers = m_descriptors;
timeval timeout{TimeOut,0};
m_error =
select(m_max+1, &readers, 0, 0, &timeout)==-1 ? "select" : "";
if( is_valid() )
{
bool is_active = false;
for( int i=0; i<=m_max; ++i)
{
//If this socket has been flagged as ready to be read ...
if( FD_ISSET(i, &readers) )
{
is_active = true;
//Check to see if socket is a listener
if(m_listener_array.find(i) != m_listener_array.end())
{
//Accept client
sockaddr_storage remote_addr;
auto remote = (sockaddr*) &remote_addr;
socklen_t len = sizeof(remote_addr);
int new_sock = accept(i, remote, &len);
m_error = new_sock == -1 ? "accept" : "";
if( is_valid() )
{
//Store accepted descriptor and update max val
m_descriptor_array.insert(new_sock);
FD_SET(new_sock, &m_descriptors);
m_max = new_sock > m_max ? new_sock : m_max;
}
}
//If send/recv socket and not in use by another thread ...
else if( in_use.find(i)==in_use.end())
{
in_use.insert(i);
//Lambda called in new thread: recv, run callback
auto process_msg=[&, i]()
{
//recv(recvfrom) according to stream(dgram)
sockaddr_storage remote_addr;
auto remote = (sockaddr*)(&remote_addr);
socklen_t len = sizeof(remote_addr);
int bytes = 0;
char buf[BufferSize];
if(SockType==SOCK_STREAM || m_connected)
{
bytes = recv(i, buf, sizeof(buf), 0);
}
else
{
bytes=recvfrom(i,buf,sizeof(buf),0, remote,
&len);
}
//Execute callback, passing in recvd msg
if( bytes != 0 )
{
Message msg(buf, i, &remote_addr);
callback( msg, this );
}
//Return {descriptor, bytes_recvd} to parent thread
return std::make_pair(i, bytes);
};
//Store thread's future to prevent it blocking
threads.push_back(std::async(std::launch::async,
process_msg));
//Update thread count and block if max reached
++thread_count;
wait_for_threads();
}
}
}
//select() timed out (0 readers), delegate action to callback
if(is_active == false)
{
char buf[] ="Sockets::Socket::SockMsg: No activity";
Message msg(buf);
callback(msg, this);
}
}
//Wait for completion of threads if thread count max is reached
wait_for_threads();
//Loop over{descriptor,bytes_recvd}, close if 0 bytes(conn dropped)
for( auto&& res : desc_bytes )
{
if( res.second == 0 )
{
close(res.first);
m_descriptor_array.erase(res.first);
FD_CLR(res.first, &m_descriptors);
}
}
desc_bytes.clear();
in_use.clear();
}
for(auto&& listener : m_listener_array) close(listener);
m_listener_array.clear();
for(auto&& descriptor : m_descriptor_array) close(descriptor);
m_descriptor_array.clear();
}
/**
* Inserts messages onto socket for send/sendto
*
* @param msg {Message&}
*
* Usage example:
*
* message << msg_1 << msg_2;
*/
Socket& operator<<( Message& msg )
{
//Extract socket, port+IP and msg string from msg arg
int descriptor=0;
sockaddr_storage *addr;
std::pair<const char*, int> buf;
msg >> std::reference_wrapper<int>(descriptor)
>> std::reference_wrapper<sockaddr_storage*>(addr)
>> buf;
//Set up fd set for select
int bytes = 1;
fd_set writer;
FD_ZERO(&writer);
FD_SET(descriptor, &writer);
//While partial send and no send error (bytes<=0)
while(buf.second > 0 && bytes > 0)
{
//Block until socket ready for write
select(descriptor+1, 0, &writer, 0, 0);
//send/sendto if stream/dgram
if( SockType == SOCK_STREAM || m_connected)
{
bytes = send(descriptor, buf.first, buf.second, 0);
}
else
{
bytes = sendto(descriptor, buf.first, buf.second, 0,
(sockaddr*)(addr),sizeof(*addr));
}
buf.first += bytes;
buf.second -= bytes;
}
//Return *this to allow message << msg_1 << msg_2 ...
return *this;
}
/**
* Shutdowm client or server
*/
void quit( void )
{
m_quit = true;
}
/**
* Checks whether client or server is running
*
* @return {bool} - true iff running
*/
bool is_running( void )
{
return m_quit == false;
}
/**
* Get descriptors of listener sockets
*
* @return {const std::vector<int>&} list of listener descriptors
*/
const std::vector<int>& get_listeners( void )
{
return m_listener_array;
}
/**
* Get descriptors of all sockets
*
* @return {const std::vector<int>&} list of descriptors
*/
const std::vector<int>& get_descriptors( void )
{
return m_descriptor_array;
}
/**
* Returns the remote/local machine (peer/host) address for the socket.
* @param descriptor {int} - socket descriptor
* @return {SocketInfo<>} - the address container for remote/local machine
*
* Usage example:
* SocketInfo<host> info = my_socket[sock_descriptor]; n
* sockaddr_storage *host = info; n
* or n
* sockaddr_storage *host=(SocketInfo<peer>)my_socket[sock_descriptor];
*/
SocketInfo<> operator[]( int descriptor )
{
auto deb = SocketInfo<>(descriptor);
return SocketInfo<>(descriptor);
}
/**
* Get error that terminated client or server
*
* @return {const std::string&} - the error. If no error: ""
*/
const std::string& get_error( void )
{
return m_error;
}
private:
std::atomic<bool> m_quit;
std::string m_error;
addrinfo m_hints;
int m_max = 0;
bool m_connected;
fd_set m_descriptors;
std::unordered_set<int> m_listener_array;
std::unordered_set<int> m_descriptor_array;
};
/**
* This function is a substitute for gethostname and gethostbyname, which were
* found not to work correctly within a VMWare virtual machine
*
* @param {std::string& addr} The IP address of this host.
*
* Notes: If the internet is disconnected, "Error: No internet" is returned.
*/
void get_ip(std::string& addr)
{
int file_descriptors[2];
pipe(file_descriptors);
if(!fork())
{
close(1);
dup(file_descriptors[1]);
close(file_descriptors[0]);
execlp("hostname", "hostname", "-I", NULL);
}
else
{
char buf[256];
close(0);
dup(file_descriptors[0]);
close(file_descriptors[1]);
auto bytes = read(file_descriptors[0], buf, sizeof(buf));
buf[bytes]='0';
std::string str(buf);
str.erase(remove_if(str.begin(), str.end(), isspace), str.end());
addr = std::string(buf).find('.') == std::string::npos ?
"Error: No internet" : str;
}
}
/**
* Gets the port from sockaddr_storage.
*
* @param storage {const sockaddr_storage& }
* @return {std::string} - the port
*/
std::string get_port(const sockaddr_storage& storage)
{
return std::to_string(ntohs( ((sockaddr*)&storage)->sa_family == AF_INET ?
(((sockaddr_in*)&storage)->sin_port) :
(((sockaddr_in6*)&storage)->sin6_port)) );
}
/**
* Gets the IP from sockaddr_storage.
*
* @param storage {const sockaddr_storage& }
* @return {std::string} - the IP
*/
std::string get_address(const sockaddr_storage& storage)
{
char str[256];
inet_ntop( storage.ss_family,
((sockaddr*)&storage)->sa_family == AF_INET ?
(char*)&(((sockaddr_in*)&storage)->sin_addr) :
(char*)&(((sockaddr_in6*)&storage)->sin6_addr),
str, sizeof(str));
return str;
}
/**
* Gets the port & IP from sockaddr_storage.
*
* @param storage {const sockaddr_storage& }
* @return {std::pair<std::string, std::string>} - the {port,IP} pair
*/
std::pair<std::string, std::string>
get_port_and_address(const sockaddr_storage& storage)
{
return std::make_pair(get_port(storage), get_address(storage));
}
};
/**
* @}
*/
/**
* addtogroup UnitTest
* @{
*/
namespace UnitTest
{
/**
* This class provides basic unit testing capability.
*
*tparam {T} type of variable being verified
*
* Usage:
*
* Verify<MyType>("Verifying my_float: ", my_variable) == expected_value
*
*/
template< typename T=int> struct Verify
{
/**
* @param {T} - The type of the variable being verified
* @param name {const std::string&} - The header to precede each checkpoint
* @param actual {const T& } - The actual value of the variable verified
*
* Notes: This is used in conjunction with operator==().
*
* Example: Verify<float>("Verifying my_float: ", my_float) == expected_value;
*/
Verify( const std::string& name, const T& actual ) :
m_name(name),
m_actual(actual),
m_banner( "" )
{
}
/**
* @param name {const std::string&} - Banner message
*
* Usage example:
*
* void test_message_class() n
* { n
* Verify<> banner("Message class"); //prints banner b4 test output n
* tests(); n
* //On exit dtor of "banner" prints banner after test output
* n }
*/
Verify(const std::string& str = "") :
m_name(""),
m_actual(T()),
m_banner( str )
{
//Run banner at start of tests
if( str.empty() == false )
{
banner( str );
}
}
/**
* Usage example:
* void test_message_class() n
* { n
* Verify<> banner("Message class"); //prints banner b4 test output n
* tests(); n
* //On exit dtor of "banner" prints banner after test output n
* }
*/
~Verify(void)
{
//Run banner at end of tests
if( m_banner.empty() == false )
{
banner( m_banner, false );
}
}
/**
* Returns number of successful tests
*
* @return {unsigned&} - number of tests that passed
*
* Usage example:
*
* int main() n
* { n
* tests(); n
* std::cout << "Number of tests that passed=" << Verify<>::passes();
* n }
*/
static
unsigned& passes( void )
{
static unsigned pass = 0;
return pass;
}
/**
* Returns total number of tests
*
* @return {unsigned&} - total number of tests
*
* Usage example:
*
* int main() n
* { n
* tests(); n
* std::cout << "Total number of tests that failed=" << Verify<>::total(); n
* }
*/
static
unsigned& total( void )
{
static unsigned tot = 0;
return tot;
}
/**
* Verifies an expected value against an actual value, used with ctor
*
* Usage:
*
* Verify<MyType>("Verifying my_float: ", my_variable) == expected_value
*/
bool operator==(const T& expected)
{
//Update total number of tests
bool passed = true;
++Verify<>::total();
//Print results of comparison
// "test message" (Expected: exp) == (Actual: act) passed/failed
std::stringstream ssname, ss;
ssname << m_name << ": ";
ss << std::left << std::setw(65) << ssname.str()
<< "(expected: " << """ << expected <<"")"
<< " == "
<< "(actual: " << """ << m_actual << "")";
std::cout << std::left << std::setfill('-') << std::setw(150) << ss.str();
if(expected == m_actual)
{
++Verify<>::passes();
std::cout << "->passed" << std::endl;
}
else
{
passed = false;
std::cout << "->failed" << std::endl;
}
//Return whether pass or fail
return passed;
}
private:
/**
* Prints banner at start ("Testing" msg_string)
* and end of block of tests ("Tests completed for " msg_string)
*/
static
void banner( const std::string& msg, bool begin = true )
{
std::cout << "====================================================="
<< std::endl
<< (begin ? "Testing " : "Tests completed for ")
<< msg
<< std::endl
<< "====================================================="
<< std::endl;
}
std::string m_name;
const T& m_actual;
const std::string m_banner;
};
/**
* Dummy struct used for Verify<> specialisation
*/
struct Results{};
/**
* This specialisation simply prints out total number of tests and successes
* when program exits.
*
* Usage example:
* int main() n
* { n
* Verify<> tests; n
* various_tests(); n
* //on exit, dtor of "tests" prints out total number of tests + successes
* n }
*/
template<> struct Verify<Results>
{
~Verify( void )
{
std::cout << "Total: " << Verify<>::total()
<< ", passes: " << Verify<>::passes()
<< std::endl;
}
};
/**
* Helper function that creates and returns a Verify<Type> object for use with
* == expected_value
*
* tparam {T} deduced type of variable being verified
* @param name {const std::string&} meesage used to identify test in output
* @param var {const T& } - actual value of variable to be == with expected val
*
* Usage example:
*
* verify("Verifying my_float: ", my_variable) == expected_value
*/
template<typename T>
Verify<T> verify(const std::string& name, const T& var)
{
return Verify<T>(name, var);
}
/**
* Helper macros that insert function name of caller into test identifier
* passed to verify(id, var)
*/
#define VERIFY(msg, var) 
verify(std::string(__PRETTY_FUNCTION__)+" ["+#var+"] "+msg,var)
}
/**
* @}
*/
/**
* addtogroup Tests
* @{
*/
namespace Tests
{
/**
* Tests of Message class
*/
void test_message( void )
{
using namespace UnitTest;
//Print test banner
Verify<> banner( "Message class" );
using namespace Sockets;
//Create msg and add an int and a string to be sent
sockaddr_storage addr;
char buf[256];
Message msg(buf, 10, &addr);
std::string str(" Str with spaces ");
msg << 1234 << str;
//Extract descriptor, addr, int and string back from msg
int test_descriptor=0, test_int=0;
sockaddr_storage *test_addr;
std::string test_str = "";
const char *test_buf;
msg >> std::reference_wrapper<int>(test_descriptor)
>> test_int
>> std::reference_wrapper<sockaddr_storage*>(test_addr)
>> test_str
>> std::reference_wrapper<const char*>(test_buf);
//Verify that values extracted match values inserted
VERIFY("Test Descriptor", test_descriptor) == 10;
VERIFY("Test int", test_int) == 1234;
VERIFY("Test addr", test_addr) == &addr;
VERIFY("Test str", test_str) == str;
VERIFY("Test buf", (float*)test_buf) == (float*)buf;
}
/**
* Tests of Socket class
*/
template<int Type>
void test_socket( const std::string& addr="", bool is_connected=false,
bool multicast = false
)
{
using namespace UnitTest;
//Multicast if set true and not a connected dgram
multicast = multicast && !is_connected;
//Prepare msg string according to msg type
std::string test = is_connected ? "Connected " : "";
test += Type == SOCK_STREAM ? "sock stream" : multicast == false
? "datagram" :
"multicast";
//Prepare msg string according to whether server or client
Verify<> banner(test + " Client/Server");
using namespace Sockets;
//Create server socket
Socket<Type, server> server_socket;
//Create thread function to run server & keep count of msgs recvd
unsigned server_msg_count = 0;
auto server_callback =
[&server_socket, multicast, &server_msg_count, is_connected]()
{
//Specify port and addr (blank or multicast) for server
std::vector<PortIpRequestBacklog>
vec{PortIpRequestBacklog("10000", multicast ? "/225.0.0.37":"")};
server_socket.create(vec);
//Create callback for recvd msgs
auto callback =
[&server_socket, &server_msg_count, multicast]( Message& msg,
decltype(server_socket) *sock)
{
#define __PRETTY_FUNCTION__ "server::callback()"
//Extract msg string
std::pair<const char*, int> str;
msg >> str;
//If msg is not a "no activity" msg from server ...
if(std::string(str.first)!="Sockets::Socket::SockMsg: No activity")
{
//Define expected msg based on msg count
std::string tmp = server_msg_count==0 ?
"(1) Client request" : "(3)
QUIT";
std::string func = Type == SOCK_STREAM ?
"[void sock_stream_server()] Client req msg" :
"[void sock_dgram_server()] Client req msg";
//Verify msg is the one expected
VERIFY("Test server recv", std::string(str.first))==tmp.c_str();
//Verify address is correct for stream
if(Type==SOCK_STREAM)
{
//Get socket
int desc;
msg >> std::reference_wrapper<int>(desc);
//Get host IP
std::string addr;
get_ip(addr);
//Get address container
sockaddr_storage *host_addr =
(SocketInfo<host>)(server_socket[desc]);
//Verify server(host) port &addr and client(peer) addr
VERIFY("Host port",get_port_and_address(*host_addr).first)
== "10000";
VERIFY("Host address",get_port_and_address(*host_addr).second)
== addr;
sockaddr_storage *peer_addr =
(SocketInfo<peer>)server_socket[desc];
VERIFY("Peer address",get_port_and_address(*peer_addr).second)
== addr;
}
//Create response and send back to client via socket
std::string resp = "(2) Server response";
if(server_msg_count==0)
{
++server_msg_count;
msg << std::make_pair<const char*,int>(resp.c_str(),
resp.size()+1);
*sock << msg;
}
else
{
//"no activity", assume client offline & shut down server
sock->quit();
}
}
else
{
//Have received all msgs, shut down server
sock->quit();
}
#undef __PRETTY_FUNCTION__
};
//Start server running with above callback function
server_socket.run(callback);
};
//Launch thread to run server, store returned future to prevent blocking
auto res_server = std::async(std::launch::async, server_callback);
//Wait for server to be in "ready" state before starting client
while(server_socket.is_running() == false)
{
std::this_thread::yield();
}
//Create client. is_connected=true only applied if dgram
Socket<Type, client> client_socket(is_connected);
//Create thread function to run client
auto client_callback = [&client_socket, multicast, is_connected, addr]()
{
//Specify server port & addr/multicast addr to client + handshake str
std::vector<PortIpRequestBacklog> vec{PortIpRequestBacklog("10000",
multicast ? "/225.0.0.37" : addr, "(1) Client request")};
client_socket.create(vec);
//Create callback for msgs recvd by client
auto callback =[&client_socket, multicast, is_connected]
( Message& msg, decltype(client_socket)*)
{
#define __PRETTY_FUNCTION__ "client::callback()"
std::pair<const char*, int> str;
msg >> str;
//If not "no activity" msg
if(std::string(str.first)!="Sockets::Socket::SockMsg: No activity")
{
//Verify msg is correct
std::string tmp = "(2) Server response";
std::string func = Type == SOCK_STREAM ?
"[void sock_stream_client()] Server resp msg" :
"[void sock_dgram_client()] Server resp msg";
VERIFY("Server resp msg", std::string(str.first))==tmp;
//Verify address is correct (only for strem or connected dgram
if(Type==SOCK_STREAM || is_connected)
{
//Get socket
int desc;
msg >> std::reference_wrapper<int>(desc);
//Get host & peer address containers
sockaddr_storage *host_addr =
(SocketInfo<host>)(client_socket[desc]);
sockaddr_storage *peer_addr =
(SocketInfo<peer>)client_socket[desc];
//Get host ip
std::string addr;
get_ip(addr);
//Get address of socket
sockaddr_storage *p ;
//Verify client(host) addr and server(peer) port & addr
msg >> std::reference_wrapper<sockaddr_storage*>(p);
VERIFY("Host address",get_port_and_address(*host_addr).second)
== addr;
VERIFY("Peer port",get_port_and_address(*peer_addr).first)
== "10000";
VERIFY("Peer address",get_port_and_address(*peer_addr).second)
== addr;
}
//Send response back to server, telling it to shut down
std::cout<<"Shutting down server. Please wait ..."<<std::endl;
std::string resp = "(3) QUIT";
msg << std::make_pair<const char*,int>(resp.c_str(),
resp.size()+1);
client_socket << msg;
//Resend, to verify that app doesn't crash if server offline
resp = "(4) QUIT";
msg << std::make_pair<const char*,int>(resp.c_str(),
resp.size()+1);
client_socket << msg;
}
else
{
// "no activty", assume server is offline & shut down client
client_socket.quit();
}
#undef __PRETTY_FUNCTION__
};
client_socket.run(callback);
};
//Run client in a thread, store returned future to prevent blocking
auto res_client =
std::async(std::launch::async, client_callback);
//Wait for server thread to exit
res_server.wait();
std::cout << "Server is shut down" << std::endl;
//Shut down client
std::cout << "Shutting down client. Please wait ..." << std::endl;
client_socket.quit();
//Wait for client to shut down
while(client_socket.is_running() == true)
{
std::this_thread::yield();
}
//Wait for client thread to exit
res_client.wait();
std::cout << "Client is shut down" << std::endl;
}
}
/**
* @}
*/
int main( void )
{
using namespace Sockets;
using namespace UnitTest;
using namespace Tests;
//Run tests of Message class
Verify<Results> results;
test_message();
//Get ip of this machine
std::string addr;
get_ip(addr);
std::cout << std::endl << "Using IP = "" << addr << """ << std::endl;
//Run sock stream client/server tests
test_socket<SOCK_STREAM>(addr);
//Run unconnected dgram client/server tests
test_socket<SOCK_DGRAM>(addr);
//Run multicast dgram client/server tests
test_socket<SOCK_DGRAM>(addr, false, true);
//Run connected dgram client/server tests
test_socket<SOCK_DGRAM>(addr, true);
return 0;
}

Multithreaded sockets c++11

  • 1.
    /** * @mainpage * @anchormainpage * @brief * @details * @copyright Russell John Childs, PhD, 2015 * @author Russell John Childs, PhD * @date 2015-06-21 * * This file contains classes: Stream, Message, Socket, Verify. * * e Stream e: Wraps a char* buffer so that "<<" and ">>" can be used. * * e Message e: Serialises port, ip, message string to and from wrapped char buf[] * using "<<" and ">>" operators. This makes application less code intensive * when transferring data between application and socket * * e Socket e: A generic, multithreaded, multi-socket class that is used * to create a SOCK_STREAM, SOCK_DGRAM or multicast server or client * * e Verify e: A class for recording passes/fails of expected==actual. * * Compiled and tested under Linux Mint, using g++ 4.8. * * g++ options: -O0 -g3 -Wall -O0 -fopenmp -mavx -m64 -g -Wall -c * -fmessage-length=0 -fno-omit-frame-pointer --fast-math * -std=c++11 -I/opt/intel/vtune_amplifier_xe_2013/include/ * * Linker options: -Wl,--no-as-needed -fopenmp * -L/opt/intel/vtune_amplifier_xe_2013/lib64/ * * Cmd line options: -lpthread -latomic -littnotify -ldl * * Documentation: Doxygen comments for interfaces, normal for impl. * * @file sockets.cpp * @see * @ref mainpage */ #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unordered_map> #include <unordered_set> #include <vector> #include <queue> #include <string> #include <sstream> #include<functional> #include <future> #include <mutex> #include <iostream> #include <iomanip> #include <algorithm> #include <cxxabi.h> #include <string.h> /** * addtogroup Sockets * @{ */ namespace Sockets
  • 2.
    { enum ApplicationType { server =0, /**< Creates a server */ client = 1 /**< Creates a client */ }; /** * This class wraps a buffer, char buf[Size], so that it may be used with * iostream "<<" and ">>" */ struct Stream : public std::streambuf { /** * Sets range of put and get pointers in buf[] * tparam Size {int} - deduced size of wrapped buffer char buf[Size] */ template<int Size> Stream( char (&buffer)[Size]) { setg(buffer, buffer, buffer+Size); setp(buffer, buffer+Size); } ~Stream( void ) { } /** * Override of virtual function. * @see std::streambuf::underflow */ int_type underflow( void ) { return gptr() == egptr() ? traits_type::eof() : traits_type::to_int_type(*gptr()); } /** * Override of virtual function. * @see std::streambuf::overflow */ int_type overflow( void ) { return pptr() == epptr() ? traits_type::eof() : traits_type::to_int_type(*epptr()); } }; /** * Serialises data between buffer[] and socket, via operators "<<" and ">>".n * Usage example:n * std::pair<const char*, int> buf;n * std::string buf_1; * * message >> reference_wrapper<int>(descriptor) >> buf >> * reference_wrapper<sockaddr_storage*>(addr); //order independent, vars optl * n * message_1 >> reference_wrapper<int>(descriptor) >> buf_1 >> * reference_wrapper<sockaddr_storage*>(addr); //order independent, vars opt * * message_2 << buf << reference_wrapper<int>(descriptor) << * reference_wrapper<sockaddr_storage*>(addr); //order independent, vars opt * n * message_3 << reference_wrapper<sockaddr_storage*>(addr) <<
  • 3.
    * reference_wrapper<int>(descriptor) <<buf_1; //order independent, vars opt */ struct Message { /** * tparam Size {int} - size of wrapped buffer (NB: deduced) * @param buffer {char (&)[Size]} - Reference to wrapped char buffer[] * @param descriptor {int} - Socket descriptor * @param address {sockaddr_storage*} - Client or Server address of socket */ template<int Size> Message( char (&buffer)[Size], int descriptor=-1, sockaddr_storage *address=0 ) : m_buffer(buffer), m_size(Size), m_stream(buffer), m_descriptor(descriptor), m_address(address), m_in(&m_stream), m_out(&m_stream) { } ~Message( void ) { } /** * Extractor that extracts values from wrapped buffer to "msg" argument. * tparam T - type of variable to be extracted to (deduced) * @param msg {T} - variable to extract to * * Usage: * * my_message >> my_int >> my_float >> my_string; */ template< typename T> Message& operator>>(T& msg) { m_in >> msg; m_in.get(); return *this; } /** * Extractor that copies wrapped buffer to "msg" argument. * @param msg {std::string&} - variable to extract to. * * Notes: Preserves, rather than skips, whitespace. */ Message& operator>>(std::string& msg) { std::getline(m_in, msg); m_in.get(); return *this; } /** * Extractor that extracts socket descriptor. * @param descriptor {std::reference_wrapper<int>} - * variable to extract to * * Notes: std::reference_wrapper is used to disambiguate * ">> int_from_client" and ">> reference_wrapper<int>(descriptor)" * */
  • 4.
    friend Message& operator>>(Message& in,std::reference_wrapper<int> descriptor) { descriptor.get() = in.m_descriptor; return in; } /** * Extractor that extracts socket's client/server address. * @param address {std::reference_wrapper<sockaddr_storage*>} - * variable to extract to. * * The argument is a pointer, so that it can be used with UNIX sockets. */ friend Message& operator>>(Message& in, std::reference_wrapper<sockaddr_storage*> address) { address.get() = in.m_address; return in; } /** * Extractor that extracts wrapper buffer to char* msg. * @param msg {const char*} - variable to extract to */ friend Message& operator>>(Message& in, const char*& msg) { msg = in.m_buffer; return in; } /** * Extractor that extracts wrapper buffer & size. * @param msg {std::pair<const char*, int>&} - variable to extract to */ friend Message& operator>>(Message& in, std::pair<const char*, int>& msg) { msg.first = in.m_buffer; msg.second = in.m_size; return in; } /** * Inserter that inserts values into wrapped buffer to "msg" argument. * tparam T type of variable to insert from * @param msg {T} - variable to insert from * * Usage: * * my_message << my_int << my_float << my_string; */ template< typename T> Message& operator<<(const T& msg) { m_out << msg << std::endl; return *this; } /** * Inserter that copies wrapper buffer & size. * @param {std::pair<const char*, int>&} msg - variable to insert from */ friend
  • 5.
    Message& operator<<(Message& in, conststd::pair<const char*, int>& msg) { in.m_buffer = msg.first; in.m_size = msg.second; return in; } private: const char* m_buffer; int m_size; Stream m_stream; int m_descriptor; sockaddr_storage *m_address; std::istream m_in; std::ostream m_out; }; /** * Helper that stores destination port, IP, request string to initiate * handshake and socket backlog */ struct PortIpRequestBacklog { /** * @param port {const std::string&} * @param ip {const std::string&} * @param request {const std::string&} * @param backlog {int} */ PortIpRequestBacklog( const std::string& port, const std::string& ip="", const std::string& request="", int backlog=10) : m_port(port), m_ip(ip), m_request(request), m_backlog(backlog) { } std::string m_port; std::string m_ip; std::string m_request; int m_backlog; }; /** * Used with SocketInfo and Socket::operator[] to extract peer/host address */ enum PeerOrHost { peer = 0, /**< Signifies remote machine for socket */ host = 1, /**< Signifies local machine for socket */ undefined = 2 /**< Signifies that getpeername/getsockname not called */ }; /** * Allows Socket<>::operator[] to be overloaded by return type. * * tparam T {PeerOrHost} - whether info is for peer, host or undefined * * Usage example: * SocketInfo<host> info = my_socket[sock_descriptor]; n * sockaddr_storage *host = info; n * or n * sockaddr_storage *host=(SocketInfo<peer>)my_socket[sock_descriptor]; */
  • 6.
    template< PeerOrHost T= undefined> struct SocketInfo { SocketInfo( void ) : m_descriptor(0) { memset(&m_addr, 0, sizeof(sockaddr_storage)); } /** * Initialises descriptor and address * @param descriptor {int} - socket descriptor * * Notes: Zeroing sockaddr_storage seems cause getpeer(sock)name to work * incorrectly. */ SocketInfo( int descriptor) : m_descriptor( descriptor ) { //memset(&m_addr, 0, sizeof(sockaddr_storage)); } ~SocketInfo( void ) { } /** * Returns the remote machine (peer) address container. This mechanism * requires less code than overloading via a covariant return type. */ operator SocketInfo<peer>&( void ) { socklen_t len = sizeof(sockaddr_storage); getpeername(m_descriptor,(sockaddr*)&m_addr, &len); return *(SocketInfo<peer>*)this; } /** * Returns the local machine (host) address container. This mechanism * requires less code than overloading via a covariant return type. */ operator SocketInfo<host>&( void ) { socklen_t len = sizeof(sockaddr_storage); getsockname(m_descriptor,(sockaddr*)&m_addr, &len); return *(SocketInfo<host>*)this; } /** * Extracts address from address container structure. * */ operator sockaddr_storage*( void ) { return &m_addr; } //private: const int m_descriptor; sockaddr_storage m_addr; }; /** * This encapsulates the creation of a list of one or more sockets, of the * same type: stream, datagram, multicast datagram, or connected datagram. * The class may used to create a client or a server.
  • 7.
    * * Usage example: *//Create a stream server n * Socket<SOCK_STREAM, server> server_socket; n * server_socket.create(vector<PortIpRequestBacklog>(port)); * server_socket.run(callback_func_for_server_recv); //Call within thread * * //Create a connected dgram client n * Socket<SOCK_DGRAM, client> client_socket(true); //false/true is un/connected * n //create(...), run(callback_func_for_client_recv) in thread * * //Create a multicast dgram server ('/' marks start of multicast ip) n * Socket<SOCK_DGRAM, server> server_socket_1; n * server_socket.create(vector<PortIpRequestBacklog>(port, "/225.0.0.37")); n * //create(...), run(...) in thread * * //Create a multicast dgram client ('/' marks start of multicast ip) n * Socket<SOCK_DGRAM, client> client_socket_1; n * server_socket.create(vector<PortIpRequestBacklog>(dest_port,"/225.0.0.37")); * n //create(...), run(...) in thread * * tparam SockType {int} SOCK_STREAM | DGRAM - type of socket * tparam AppType {ApplicationType} server | client - App is client or server * tparam Family {int} - internet family (see addrinfo) * tparam BufferSize {int} - size of buffer used to receive/send TCP/UDP msg * * @see man getaddrinfo * * Notes: * * In this implementation, a server or client sends and receives on the * same socket. This is to permit multithreading without locks. In a future * implementation they will be able to send a single msg to multiple * sockets. * * The current implementation allows a client to multicast to * multiple servers on the same socket from which it receives a msg, but * all servers must have the same port number. * * The user may effect sending of a single msg to multiple sockets, but * must provide their own thread-safety locks and record which socket * descriptor has been allocated to each server address. */ template< int SockType=SOCK_STREAM, ApplicationType AppType=client, int Family=AF_UNSPEC, int BufferSize = 256> class Socket { public: /** * @param connected {bool} - false/true is un/connected. Used with DGRAM. * @param flags {int} - addrinfo flags * @param prot {int} - internet protocol (see addrinfo) * @see man getaddrinfo */ Socket( bool connected=false, int flags=0, int prot=0) : m_quit(true), m_error(""), m_hints{flags, Family, SockType, prot}, m_max(0), m_connected(connected) { FD_ZERO(&m_descriptors); } ~Socket( void ) {
  • 8.
    } /** * Checks ifthe app is running or has exited due to error. If no error has * occurred it returns true. If quit() has been called on the app, then * is_valid() will return true, unless include_quit is set, when it will * return false. * * @param include_quit {bool} - If set, returns false if quit() was called */ bool is_valid( bool include_quit=true ) { return !(include_quit && m_quit == true) && (m_error == ""); } /** * Creates the socket * * @param in {const PortIpRequestBacklog&} - server/dest port, IP, string to * be sent from client to server to start handshake, backlog val for socket * * Notes: For a multicast the IP should begin with '/'. Eg: "/225.0.0.37". */ void create(const PortIpRequestBacklog& in) { //Extract port, IP, handshake string, backlog const std::string& port = in.m_port; const std::string& ips = in.m_ip; int backlog = in.m_backlog; const std::string& request = in.m_request; //Extract multicast IP, if it exists auto split = [](const std::string& in, std::string& a, std::string& b) { auto p = in.find("/"); a = p == std::string::npos ? in : in.substr(0,p); b = p == std::string::npos ? "" : in.substr(p+1,in.size()-p-1); }; std::string ip, ip_multicast; split(ips, ip, ip_multicast); ip = AppType==server || ip_multicast.empty()==true ? ip : ip_multicast; //m_hints.ai_flags = ip.size()==0 ? AI_PASSIVE : 0; m_hints.ai_flags =ip.size()==0 && AppType==server ? AI_PASSIVE : 0; //Get addrinfo struct, set error if unsccessful addrinfo *info; const char *url = ip.empty() ? 0 : ip.data(); m_error= getaddrinfo(url, port.data(), &m_hints, &info) != 0 ? "getaddrinfo":""; //Loop over addresses while no error, addr not null auto tmp = info; int sock = -1; while( is_valid( false ) && tmp != NULL && sock < 0 ) { //Make socket sock = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); m_error = sock < 0 ? "socket" : ""; if(is_valid( false )) { //Set socket options setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &tmp,
  • 9.
    sizeof(int) ); //Bind socketto port, if server if(AppType == server) { m_error = bind(sock,tmp->ai_addr,tmp->ai_addrlen)>= 0 ? "" : "bind"; } //Connect to server if app is a client and stream/conn dgram else if(SockType == SOCK_STREAM || m_connected) { m_error=connect(sock,tmp->ai_addr,tmp->ai_addrlen)>=0 ? "" : "connect"; //Send handshake string to stream/conn dgram server if(is_valid( false ) && request.empty()==false) { m_error = send(sock, request.c_str(), request.size()+1, 0)>0? "" : "send"; } } else { //Send handshake string to unconn dgram server if(is_valid( false ) && request.empty()==false) { m_error = sendto(sock, request.c_str(), request.size()+1, 0, tmp->ai_addr, tmp->ai_addrlen) > 0 ? "" : "sendto"; } } //Close socket, set error if bind unsuccessful if(is_valid( false ) == false) { close(sock); sock = -1; tmp = tmp->ai_next; m_error = tmp == 0 ? "bind" : ""; } //Subscribe to multicast group if "listener" (server) else if( ip_multicast.empty() == false && AppType == server) { ip_mreq mreq; mreq.imr_multiaddr.s_addr=inet_addr(ip_multicast.data()); mreq.imr_interface.s_addr=htonl(INADDR_ANY); m_error = setsockopt(sock,IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))>=0 ? "" :"multicast"; } } } if( is_valid( false ) ) { freeaddrinfo(info); // If server and stream start listener, record descriptor, // set up fd set and max descriptor val for select() if( AppType == server && SockType == SOCK_STREAM) { m_error = listen(sock, backlog) == -1 ? "listen" : "";
  • 10.
    if( is_valid( false) ) { m_listener_array.insert(sock); FD_SET( sock, &m_descriptors ); m_max = sock; } } //If client or dgram, record descriptor, // set up fd set and max descriptor val for select() else { m_descriptor_array.insert(sock); FD_SET( sock, &m_descriptors ); m_max = sock; } } } /** * Creates multiple sockets, each associated with its own port (if server) * or port and dest address (if client) * * @param in {const std::vector<PortIpRequestBacklog>&} - list of port, * <IP>, handshake string, backlog for each of the sockets to be created */ void create( const std::vector<PortIpRequestBacklog>& in) { for(auto&& i : in ) { create(i); } } enum DescriptorType { listeners = 0, /**< Descriptors will be for listener sockets */ descriptors = 1 /**< Descriptors will be for send/recv sockets */ }; /** * Sets the socket(s) to be running, i.e. ready to accept connections and * send/recv msgs * * tparam Functor {void(Message& message)} - Callback function to be executed upon recv/recvfrom of msg. * par * The msg is passed to the callback as a Message& * argument. The callback may extract the port, IP, msg string of sender * and return a response via Message::operator<<(...), * par * eg message << some_string; */ template< typename Functor, unsigned ThreadCount=1, unsigned TimeOut=5 > void run( Functor& callback) { m_quit = false; //Keep track of num of threads, {descriptor, bytes_recv} returned as //futures by std::async threads unsigned thread_count = 0; std::pair<int, int> descriptor_bytes_recv; std::vector<std::future<std::pair<int, int>> > threads; std::vector<std::pair<int, int>> desc_bytes; std::unordered_set<int> in_use; //Lambda that blocks if max thread count reached
  • 11.
    auto wait_for_threads =[&]() { if( thread_count == ThreadCount ) { //If max reached, wait for completion of threads for( auto&& res : threads ) { res.wait(); //store {descriptor, bytes_recvd} returned by thread desc_bytes.push_back(res.get()); } //Clear list of futures and thread count threads.clear(); thread_count = 0; } }; while(is_valid()) { //Block until a socket is ready for reading auto readers = m_descriptors; timeval timeout{TimeOut,0}; m_error = select(m_max+1, &readers, 0, 0, &timeout)==-1 ? "select" : ""; if( is_valid() ) { bool is_active = false; for( int i=0; i<=m_max; ++i) { //If this socket has been flagged as ready to be read ... if( FD_ISSET(i, &readers) ) { is_active = true; //Check to see if socket is a listener if(m_listener_array.find(i) != m_listener_array.end()) { //Accept client sockaddr_storage remote_addr; auto remote = (sockaddr*) &remote_addr; socklen_t len = sizeof(remote_addr); int new_sock = accept(i, remote, &len); m_error = new_sock == -1 ? "accept" : ""; if( is_valid() ) { //Store accepted descriptor and update max val m_descriptor_array.insert(new_sock); FD_SET(new_sock, &m_descriptors); m_max = new_sock > m_max ? new_sock : m_max; } } //If send/recv socket and not in use by another thread ... else if( in_use.find(i)==in_use.end()) { in_use.insert(i); //Lambda called in new thread: recv, run callback auto process_msg=[&, i]() { //recv(recvfrom) according to stream(dgram) sockaddr_storage remote_addr; auto remote = (sockaddr*)(&remote_addr); socklen_t len = sizeof(remote_addr); int bytes = 0;
  • 12.
    char buf[BufferSize]; if(SockType==SOCK_STREAM ||m_connected) { bytes = recv(i, buf, sizeof(buf), 0); } else { bytes=recvfrom(i,buf,sizeof(buf),0, remote, &len); } //Execute callback, passing in recvd msg if( bytes != 0 ) { Message msg(buf, i, &remote_addr); callback( msg, this ); } //Return {descriptor, bytes_recvd} to parent thread return std::make_pair(i, bytes); }; //Store thread's future to prevent it blocking threads.push_back(std::async(std::launch::async, process_msg)); //Update thread count and block if max reached ++thread_count; wait_for_threads(); } } } //select() timed out (0 readers), delegate action to callback if(is_active == false) { char buf[] ="Sockets::Socket::SockMsg: No activity"; Message msg(buf); callback(msg, this); } } //Wait for completion of threads if thread count max is reached wait_for_threads(); //Loop over{descriptor,bytes_recvd}, close if 0 bytes(conn dropped) for( auto&& res : desc_bytes ) { if( res.second == 0 ) { close(res.first); m_descriptor_array.erase(res.first); FD_CLR(res.first, &m_descriptors); } } desc_bytes.clear(); in_use.clear(); } for(auto&& listener : m_listener_array) close(listener); m_listener_array.clear(); for(auto&& descriptor : m_descriptor_array) close(descriptor); m_descriptor_array.clear(); } /** * Inserts messages onto socket for send/sendto * * @param msg {Message&}
  • 13.
    * * Usage example: * *message << msg_1 << msg_2; */ Socket& operator<<( Message& msg ) { //Extract socket, port+IP and msg string from msg arg int descriptor=0; sockaddr_storage *addr; std::pair<const char*, int> buf; msg >> std::reference_wrapper<int>(descriptor) >> std::reference_wrapper<sockaddr_storage*>(addr) >> buf; //Set up fd set for select int bytes = 1; fd_set writer; FD_ZERO(&writer); FD_SET(descriptor, &writer); //While partial send and no send error (bytes<=0) while(buf.second > 0 && bytes > 0) { //Block until socket ready for write select(descriptor+1, 0, &writer, 0, 0); //send/sendto if stream/dgram if( SockType == SOCK_STREAM || m_connected) { bytes = send(descriptor, buf.first, buf.second, 0); } else { bytes = sendto(descriptor, buf.first, buf.second, 0, (sockaddr*)(addr),sizeof(*addr)); } buf.first += bytes; buf.second -= bytes; } //Return *this to allow message << msg_1 << msg_2 ... return *this; } /** * Shutdowm client or server */ void quit( void ) { m_quit = true; } /** * Checks whether client or server is running * * @return {bool} - true iff running */ bool is_running( void ) { return m_quit == false; } /** * Get descriptors of listener sockets *
  • 14.
    * @return {conststd::vector<int>&} list of listener descriptors */ const std::vector<int>& get_listeners( void ) { return m_listener_array; } /** * Get descriptors of all sockets * * @return {const std::vector<int>&} list of descriptors */ const std::vector<int>& get_descriptors( void ) { return m_descriptor_array; } /** * Returns the remote/local machine (peer/host) address for the socket. * @param descriptor {int} - socket descriptor * @return {SocketInfo<>} - the address container for remote/local machine * * Usage example: * SocketInfo<host> info = my_socket[sock_descriptor]; n * sockaddr_storage *host = info; n * or n * sockaddr_storage *host=(SocketInfo<peer>)my_socket[sock_descriptor]; */ SocketInfo<> operator[]( int descriptor ) { auto deb = SocketInfo<>(descriptor); return SocketInfo<>(descriptor); } /** * Get error that terminated client or server * * @return {const std::string&} - the error. If no error: "" */ const std::string& get_error( void ) { return m_error; } private: std::atomic<bool> m_quit; std::string m_error; addrinfo m_hints; int m_max = 0; bool m_connected; fd_set m_descriptors; std::unordered_set<int> m_listener_array; std::unordered_set<int> m_descriptor_array; }; /** * This function is a substitute for gethostname and gethostbyname, which were * found not to work correctly within a VMWare virtual machine * * @param {std::string& addr} The IP address of this host. * * Notes: If the internet is disconnected, "Error: No internet" is returned. */ void get_ip(std::string& addr) { int file_descriptors[2]; pipe(file_descriptors);
  • 15.
    if(!fork()) { close(1); dup(file_descriptors[1]); close(file_descriptors[0]); execlp("hostname", "hostname", "-I",NULL); } else { char buf[256]; close(0); dup(file_descriptors[0]); close(file_descriptors[1]); auto bytes = read(file_descriptors[0], buf, sizeof(buf)); buf[bytes]='0'; std::string str(buf); str.erase(remove_if(str.begin(), str.end(), isspace), str.end()); addr = std::string(buf).find('.') == std::string::npos ? "Error: No internet" : str; } } /** * Gets the port from sockaddr_storage. * * @param storage {const sockaddr_storage& } * @return {std::string} - the port */ std::string get_port(const sockaddr_storage& storage) { return std::to_string(ntohs( ((sockaddr*)&storage)->sa_family == AF_INET ? (((sockaddr_in*)&storage)->sin_port) : (((sockaddr_in6*)&storage)->sin6_port)) ); } /** * Gets the IP from sockaddr_storage. * * @param storage {const sockaddr_storage& } * @return {std::string} - the IP */ std::string get_address(const sockaddr_storage& storage) { char str[256]; inet_ntop( storage.ss_family, ((sockaddr*)&storage)->sa_family == AF_INET ? (char*)&(((sockaddr_in*)&storage)->sin_addr) : (char*)&(((sockaddr_in6*)&storage)->sin6_addr), str, sizeof(str)); return str; } /** * Gets the port & IP from sockaddr_storage. * * @param storage {const sockaddr_storage& } * @return {std::pair<std::string, std::string>} - the {port,IP} pair */ std::pair<std::string, std::string> get_port_and_address(const sockaddr_storage& storage) { return std::make_pair(get_port(storage), get_address(storage)); } }; /** * @}
  • 16.
    */ /** * addtogroup UnitTest *@{ */ namespace UnitTest { /** * This class provides basic unit testing capability. * *tparam {T} type of variable being verified * * Usage: * * Verify<MyType>("Verifying my_float: ", my_variable) == expected_value * */ template< typename T=int> struct Verify { /** * @param {T} - The type of the variable being verified * @param name {const std::string&} - The header to precede each checkpoint * @param actual {const T& } - The actual value of the variable verified * * Notes: This is used in conjunction with operator==(). * * Example: Verify<float>("Verifying my_float: ", my_float) == expected_value; */ Verify( const std::string& name, const T& actual ) : m_name(name), m_actual(actual), m_banner( "" ) { } /** * @param name {const std::string&} - Banner message * * Usage example: * * void test_message_class() n * { n * Verify<> banner("Message class"); //prints banner b4 test output n * tests(); n * //On exit dtor of "banner" prints banner after test output * n } */ Verify(const std::string& str = "") : m_name(""), m_actual(T()), m_banner( str ) { //Run banner at start of tests if( str.empty() == false ) { banner( str ); } } /** * Usage example: * void test_message_class() n * { n * Verify<> banner("Message class"); //prints banner b4 test output n * tests(); n * //On exit dtor of "banner" prints banner after test output n
  • 17.
    * } */ ~Verify(void) { //Run bannerat end of tests if( m_banner.empty() == false ) { banner( m_banner, false ); } } /** * Returns number of successful tests * * @return {unsigned&} - number of tests that passed * * Usage example: * * int main() n * { n * tests(); n * std::cout << "Number of tests that passed=" << Verify<>::passes(); * n } */ static unsigned& passes( void ) { static unsigned pass = 0; return pass; } /** * Returns total number of tests * * @return {unsigned&} - total number of tests * * Usage example: * * int main() n * { n * tests(); n * std::cout << "Total number of tests that failed=" << Verify<>::total(); n * } */ static unsigned& total( void ) { static unsigned tot = 0; return tot; } /** * Verifies an expected value against an actual value, used with ctor * * Usage: * * Verify<MyType>("Verifying my_float: ", my_variable) == expected_value */ bool operator==(const T& expected) { //Update total number of tests bool passed = true; ++Verify<>::total(); //Print results of comparison // "test message" (Expected: exp) == (Actual: act) passed/failed
  • 18.
    std::stringstream ssname, ss; ssname<< m_name << ": "; ss << std::left << std::setw(65) << ssname.str() << "(expected: " << """ << expected <<"")" << " == " << "(actual: " << """ << m_actual << "")"; std::cout << std::left << std::setfill('-') << std::setw(150) << ss.str(); if(expected == m_actual) { ++Verify<>::passes(); std::cout << "->passed" << std::endl; } else { passed = false; std::cout << "->failed" << std::endl; } //Return whether pass or fail return passed; } private: /** * Prints banner at start ("Testing" msg_string) * and end of block of tests ("Tests completed for " msg_string) */ static void banner( const std::string& msg, bool begin = true ) { std::cout << "=====================================================" << std::endl << (begin ? "Testing " : "Tests completed for ") << msg << std::endl << "=====================================================" << std::endl; } std::string m_name; const T& m_actual; const std::string m_banner; }; /** * Dummy struct used for Verify<> specialisation */ struct Results{}; /** * This specialisation simply prints out total number of tests and successes * when program exits. * * Usage example: * int main() n * { n * Verify<> tests; n * various_tests(); n * //on exit, dtor of "tests" prints out total number of tests + successes * n } */ template<> struct Verify<Results> { ~Verify( void ) { std::cout << "Total: " << Verify<>::total() << ", passes: " << Verify<>::passes()
  • 19.
    << std::endl; } }; /** * Helperfunction that creates and returns a Verify<Type> object for use with * == expected_value * * tparam {T} deduced type of variable being verified * @param name {const std::string&} meesage used to identify test in output * @param var {const T& } - actual value of variable to be == with expected val * * Usage example: * * verify("Verifying my_float: ", my_variable) == expected_value */ template<typename T> Verify<T> verify(const std::string& name, const T& var) { return Verify<T>(name, var); } /** * Helper macros that insert function name of caller into test identifier * passed to verify(id, var) */ #define VERIFY(msg, var) verify(std::string(__PRETTY_FUNCTION__)+" ["+#var+"] "+msg,var) } /** * @} */ /** * addtogroup Tests * @{ */ namespace Tests { /** * Tests of Message class */ void test_message( void ) { using namespace UnitTest; //Print test banner Verify<> banner( "Message class" ); using namespace Sockets; //Create msg and add an int and a string to be sent sockaddr_storage addr; char buf[256]; Message msg(buf, 10, &addr); std::string str(" Str with spaces "); msg << 1234 << str; //Extract descriptor, addr, int and string back from msg int test_descriptor=0, test_int=0; sockaddr_storage *test_addr; std::string test_str = ""; const char *test_buf; msg >> std::reference_wrapper<int>(test_descriptor) >> test_int >> std::reference_wrapper<sockaddr_storage*>(test_addr)
  • 20.
    >> test_str >> std::reference_wrapper<constchar*>(test_buf); //Verify that values extracted match values inserted VERIFY("Test Descriptor", test_descriptor) == 10; VERIFY("Test int", test_int) == 1234; VERIFY("Test addr", test_addr) == &addr; VERIFY("Test str", test_str) == str; VERIFY("Test buf", (float*)test_buf) == (float*)buf; } /** * Tests of Socket class */ template<int Type> void test_socket( const std::string& addr="", bool is_connected=false, bool multicast = false ) { using namespace UnitTest; //Multicast if set true and not a connected dgram multicast = multicast && !is_connected; //Prepare msg string according to msg type std::string test = is_connected ? "Connected " : ""; test += Type == SOCK_STREAM ? "sock stream" : multicast == false ? "datagram" : "multicast"; //Prepare msg string according to whether server or client Verify<> banner(test + " Client/Server"); using namespace Sockets; //Create server socket Socket<Type, server> server_socket; //Create thread function to run server & keep count of msgs recvd unsigned server_msg_count = 0; auto server_callback = [&server_socket, multicast, &server_msg_count, is_connected]() { //Specify port and addr (blank or multicast) for server std::vector<PortIpRequestBacklog> vec{PortIpRequestBacklog("10000", multicast ? "/225.0.0.37":"")}; server_socket.create(vec); //Create callback for recvd msgs auto callback = [&server_socket, &server_msg_count, multicast]( Message& msg, decltype(server_socket) *sock) { #define __PRETTY_FUNCTION__ "server::callback()" //Extract msg string std::pair<const char*, int> str; msg >> str; //If msg is not a "no activity" msg from server ... if(std::string(str.first)!="Sockets::Socket::SockMsg: No activity") { //Define expected msg based on msg count std::string tmp = server_msg_count==0 ? "(1) Client request" : "(3) QUIT"; std::string func = Type == SOCK_STREAM ? "[void sock_stream_server()] Client req msg" :
  • 21.
    "[void sock_dgram_server()] Clientreq msg"; //Verify msg is the one expected VERIFY("Test server recv", std::string(str.first))==tmp.c_str(); //Verify address is correct for stream if(Type==SOCK_STREAM) { //Get socket int desc; msg >> std::reference_wrapper<int>(desc); //Get host IP std::string addr; get_ip(addr); //Get address container sockaddr_storage *host_addr = (SocketInfo<host>)(server_socket[desc]); //Verify server(host) port &addr and client(peer) addr VERIFY("Host port",get_port_and_address(*host_addr).first) == "10000"; VERIFY("Host address",get_port_and_address(*host_addr).second) == addr; sockaddr_storage *peer_addr = (SocketInfo<peer>)server_socket[desc]; VERIFY("Peer address",get_port_and_address(*peer_addr).second) == addr; } //Create response and send back to client via socket std::string resp = "(2) Server response"; if(server_msg_count==0) { ++server_msg_count; msg << std::make_pair<const char*,int>(resp.c_str(), resp.size()+1); *sock << msg; } else { //"no activity", assume client offline & shut down server sock->quit(); } } else { //Have received all msgs, shut down server sock->quit(); } #undef __PRETTY_FUNCTION__ }; //Start server running with above callback function server_socket.run(callback); }; //Launch thread to run server, store returned future to prevent blocking auto res_server = std::async(std::launch::async, server_callback); //Wait for server to be in "ready" state before starting client while(server_socket.is_running() == false) { std::this_thread::yield(); }
  • 22.
    //Create client. is_connected=trueonly applied if dgram Socket<Type, client> client_socket(is_connected); //Create thread function to run client auto client_callback = [&client_socket, multicast, is_connected, addr]() { //Specify server port & addr/multicast addr to client + handshake str std::vector<PortIpRequestBacklog> vec{PortIpRequestBacklog("10000", multicast ? "/225.0.0.37" : addr, "(1) Client request")}; client_socket.create(vec); //Create callback for msgs recvd by client auto callback =[&client_socket, multicast, is_connected] ( Message& msg, decltype(client_socket)*) { #define __PRETTY_FUNCTION__ "client::callback()" std::pair<const char*, int> str; msg >> str; //If not "no activity" msg if(std::string(str.first)!="Sockets::Socket::SockMsg: No activity") { //Verify msg is correct std::string tmp = "(2) Server response"; std::string func = Type == SOCK_STREAM ? "[void sock_stream_client()] Server resp msg" : "[void sock_dgram_client()] Server resp msg"; VERIFY("Server resp msg", std::string(str.first))==tmp; //Verify address is correct (only for strem or connected dgram if(Type==SOCK_STREAM || is_connected) { //Get socket int desc; msg >> std::reference_wrapper<int>(desc); //Get host & peer address containers sockaddr_storage *host_addr = (SocketInfo<host>)(client_socket[desc]); sockaddr_storage *peer_addr = (SocketInfo<peer>)client_socket[desc]; //Get host ip std::string addr; get_ip(addr); //Get address of socket sockaddr_storage *p ; //Verify client(host) addr and server(peer) port & addr msg >> std::reference_wrapper<sockaddr_storage*>(p); VERIFY("Host address",get_port_and_address(*host_addr).second) == addr; VERIFY("Peer port",get_port_and_address(*peer_addr).first) == "10000"; VERIFY("Peer address",get_port_and_address(*peer_addr).second) == addr; } //Send response back to server, telling it to shut down std::cout<<"Shutting down server. Please wait ..."<<std::endl; std::string resp = "(3) QUIT"; msg << std::make_pair<const char*,int>(resp.c_str(), resp.size()+1); client_socket << msg; //Resend, to verify that app doesn't crash if server offline resp = "(4) QUIT"; msg << std::make_pair<const char*,int>(resp.c_str(),
  • 23.
    resp.size()+1); client_socket << msg; } else { //"no activty", assume server is offline & shut down client client_socket.quit(); } #undef __PRETTY_FUNCTION__ }; client_socket.run(callback); }; //Run client in a thread, store returned future to prevent blocking auto res_client = std::async(std::launch::async, client_callback); //Wait for server thread to exit res_server.wait(); std::cout << "Server is shut down" << std::endl; //Shut down client std::cout << "Shutting down client. Please wait ..." << std::endl; client_socket.quit(); //Wait for client to shut down while(client_socket.is_running() == true) { std::this_thread::yield(); } //Wait for client thread to exit res_client.wait(); std::cout << "Client is shut down" << std::endl; } } /** * @} */ int main( void ) { using namespace Sockets; using namespace UnitTest; using namespace Tests; //Run tests of Message class Verify<Results> results; test_message(); //Get ip of this machine std::string addr; get_ip(addr); std::cout << std::endl << "Using IP = "" << addr << """ << std::endl; //Run sock stream client/server tests test_socket<SOCK_STREAM>(addr); //Run unconnected dgram client/server tests test_socket<SOCK_DGRAM>(addr); //Run multicast dgram client/server tests test_socket<SOCK_DGRAM>(addr, false, true); //Run connected dgram client/server tests test_socket<SOCK_DGRAM>(addr, true); return 0; }