diff --git a/include/Common.hpp b/include/Common.hpp index 668251a..76d3f81 100644 --- a/include/Common.hpp +++ b/include/Common.hpp @@ -13,6 +13,9 @@ #include // runtime_error #include // ostringstream +#include // errno +#include // strerror + const long NANO = 1000000000L; // 10^9 @@ -112,4 +115,10 @@ inline void StrToT( T &t, const std::string s ) ss >> t; } + +inline std::string errnoToString(const char *s) +{ + return std::string(s).append(strerror(errno)); +} + #endif // COMMON_HPP diff --git a/include/TcpClient.hpp b/include/TcpClient.hpp new file mode 100644 index 0000000..1aa07a4 --- /dev/null +++ b/include/TcpClient.hpp @@ -0,0 +1,45 @@ +#ifndef TCP_CLIENT_HPP +#define TCP_CLIENT_HPP + +#include + +#include +#include +#include + + +class TcpClient +{ + +public: + + TcpClient ( const std::string host, + const std::string port ); + + ~TcpClient(); + + bool connect(); + bool disconnect(); + + bool send(const std::string msg); + bool receive(std::string &reply); + + +private: + + bool openSocket(); + bool closeSocket(); + + bool connectToHost(); + bool getHostInfo(struct addrinfo **servinfo); + void printHostDetails(struct addrinfo *servinfo); + bool connectToFirstAddress(struct addrinfo *servinfo); + + int m_socket; + std::string m_host; + std::string m_port; + bool m_connected; + +}; + +#endif // TCP_CLIENT_HPP diff --git a/other/client b/other/client new file mode 100755 index 0000000..d2a786c Binary files /dev/null and b/other/client differ diff --git a/src/inetSocketClient.cpp b/other/inetSocketClient.cpp similarity index 100% rename from src/inetSocketClient.cpp rename to other/inetSocketClient.cpp diff --git a/include/inetSocketClient.hpp b/other/inetSocketClient.hpp similarity index 100% rename from include/inetSocketClient.hpp rename to other/inetSocketClient.hpp diff --git a/src/localSocketClient.cpp b/other/localSocketClient.cpp similarity index 100% rename from src/localSocketClient.cpp rename to other/localSocketClient.cpp diff --git a/include/localSocketClient.hpp b/other/localSocketClient.hpp similarity index 100% rename from include/localSocketClient.hpp rename to other/localSocketClient.hpp diff --git a/other/socketClient.cpp b/other/socketClient.cpp new file mode 100644 index 0000000..21c410f --- /dev/null +++ b/other/socketClient.cpp @@ -0,0 +1,75 @@ +#include "socketClient.hpp" + +#include // errno +#include // strerror + +#include + + +SocketClient::SocketClient( const int addrDomain, + const int socketType ) + : m_connected(false) + , m_addrDomain(addrDomain) + , m_socketType(socketType) + , m_socketfd(0) +{ +} + + +SocketClient::~SocketClient() +{ + if ( m_connected && close(m_socketfd) == -1 ) + std::cerr << errorToString("ERROR closing socket. ") << std::endl; +} + + +bool SocketClient::send(const std::string msg) +{ + if ( !m_connected && !this->connect() ) + return false; + + ssize_t n = write(m_socketfd, msg.c_str(), msg.length()); + if (n == -1) { + std::cerr << errorToString("ERROR writing to socket. ") << std::endl; + return false; + } else if ( n < (ssize_t)msg.length() ) { + std::cerr << "Only a part of msg has been written to socket. " << std::endl; + return false; + } + + return true; +} + + +bool SocketClient::receive(std::string &reply) +{ + if ( !m_connected && !this->connect() ) + return false; + + char buffer[256]; + ssize_t n = read(m_socketfd, buffer, 255); + if (n == -1) { + std::cerr << errorToString("ERROR reading from socket. ") << std::endl; + return false; + } + reply = std::string(buffer, n); + + return true; +} + + +bool SocketClient::connect(void) +{ + m_socketfd = socket(m_addrDomain, m_socketType, 0); + if ( m_socketfd == -1 ) { + std::cerr << errorToString("ERROR opening socket. ") << std::endl; + return false; + } + + return connectToPeer(); +} + +std::string SocketClient::errnoToString(const char *s) const +{ + return std::string(s).append(strerror(errno)); +} \ No newline at end of file diff --git a/include/socketClient.hpp b/other/socketClient.hpp similarity index 91% rename from include/socketClient.hpp rename to other/socketClient.hpp index c6b4ac9..6551969 100644 --- a/include/socketClient.hpp +++ b/other/socketClient.hpp @@ -24,7 +24,7 @@ protected: bool connect(void); virtual bool connectToPeer(void) = 0; - std::string errorToString(const char *s) const; + std::string errnoToString(const char *s) const; bool m_connected; int m_addrDomain; diff --git a/other/tcpclient_main.cpp b/other/tcpclient_main.cpp new file mode 100644 index 0000000..6acfbb0 --- /dev/null +++ b/other/tcpclient_main.cpp @@ -0,0 +1,32 @@ +// gpp tcpclient_main.cpp -o client -I../include ../src/Logger.cpp ../src/TcpClient.cpp + +#include "TcpClient.hpp" + +#include "Logger.hpp" + +#include +#include + +int main( int argc, char * argv[] ) +{ + Logger::createInstance(); + Logger::init(std::cout); + Logger::setLogLevel(Logger::FINEST); + + TcpClient tcpclient("localhost", "4455"); + + tcpclient.connect(); + + tcpclient.send("madao"); + + std::string reply; + tcpclient.receive(reply); + std::cout << "got reply \"" << reply << "\"" << std::endl; + + tcpclient.disconnect(); + + + + Logger::destroy(); + return 0; +} \ No newline at end of file diff --git a/src/TcpClient.cpp b/src/TcpClient.cpp new file mode 100644 index 0000000..0e2e2c7 --- /dev/null +++ b/src/TcpClient.cpp @@ -0,0 +1,226 @@ +#include "TcpClient.hpp" + +#include "Logger.hpp" +#include "Common.hpp" + +#include +#include +#include // inet_ntop + + +TcpClient::TcpClient( const std::string host, + const std::string port ) + : m_socket(-1) + , m_host(host) + , m_port(port) + , m_connected(false) +{ + TRACE; +} + +TcpClient::~TcpClient() +{ + TRACE; + + disconnect(); +} + + +bool TcpClient::connect() +{ + TRACE; + + if ( !openSocket() ) + return false; + + if ( !connectToHost() ) + return false; + + m_connected = true; + return true; +} + + +bool TcpClient::disconnect() +{ + TRACE; + + if ( !closeSocket() ) + return false; + + return true; +} + + +bool TcpClient::send(const std::string msg) +{ + TRACE; + + ssize_t n = write(m_socket, msg.c_str(), msg.length()); + if (n == -1) { + LOG( Logger::ERR, errnoToString("ERROR writing to socket. ").c_str() ); + return false; + } + + return true; +} + + +bool TcpClient::receive(std::string &reply) +{ + TRACE; + + char buffer[256]; + ssize_t n = read(m_socket, buffer, 255); + if (n == -1) { + LOG( Logger::ERR, errnoToString("ERROR reading from socket. ").c_str() ); + return false; + } + reply = std::string(buffer, n); + + return true; +} + + +bool TcpClient::openSocket() +{ + TRACE; + + m_socket = socket(AF_INET, SOCK_STREAM, 0); + if ( m_socket == -1 ) { + LOG( Logger::ERR, errnoToString("ERROR opening socket. ").c_str() ); + return false; + } + + return true; +} + + +bool TcpClient::closeSocket() +{ + TRACE; + + /// @note are the return values of shutdown and close meaningful? + // if ( shutdown(m_socket, SHUT_RDWR) == -1 ) { + // LOG( Logger::ERR, errnoToString("ERROR shuting down socket. ").c_str() ); + // return false; + // } + // + // if ( close(m_socket) == -1 ) { + // LOG( Logger::ERR, errnoToString("ERROR closing socket. ").c_str() ); + // return false; + // } + + shutdown(m_socket, SHUT_RDWR); + close(m_socket); + m_socket = -1; + + return true; +} + +bool TcpClient::connectToHost() +{ + TRACE; + + struct addrinfo *results(0); + if ( !getHostInfo(&results) ) + return false; + + printHostDetails(results); + + if ( !connectToFirstAddress(results) ) + return false; + + freeaddrinfo(results); + return true; +} + + +bool TcpClient::getHostInfo(struct addrinfo **servinfo) +{ + TRACE; + + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6 + hints.ai_socktype = SOCK_DGRAM; // Datagram socket + hints.ai_flags = AI_PASSIVE; // For wildcard IP address + hints.ai_protocol = 0; // Any protocol + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + + struct addrinfo *results; + int status = getaddrinfo(m_host.c_str(), m_port.c_str(), &hints, &results); + + if (status != 0) { + LOG( Logger::ERR, std::string("Error at network address translation: "). + append(gai_strerror(status)).c_str() ) ; + return false; + } + + *servinfo = results; + return true; +} + + +void TcpClient::printHostDetails(struct addrinfo *servinfo) +{ + TRACE; + + int counter(0); + for ( struct addrinfo *it = servinfo; it != 0; it = it->ai_next) { + + counter++; + void *addr; + std::string ipver; + + if ( it->ai_family == AF_INET) { // IPv4 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)it->ai_addr; + addr = &(ipv4->sin_addr); + ipver = "IPv4"; + } else { // IPv6 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)it->ai_addr; + addr = &(ipv6->sin6_addr); + ipver = "IPv6"; + } + char ipstr[INET6_ADDRSTRLEN]; + inet_ntop( it->ai_family, addr, ipstr, sizeof ipstr ); + + LOG( Logger::DEBUG, std::string(TToStr(counter)).append(". address is "). + append(ipver).append(": "). + append(ipstr).c_str() ); + } +} + + +bool TcpClient::connectToFirstAddress(struct addrinfo *servinfo) +{ + TRACE; + + for ( struct addrinfo *it = servinfo; it != 0; it = it->ai_next) + if (::connect(m_socket, it->ai_addr, it->ai_addrlen) != -1) { + char hostBuffer[256]; + char serviceBuffer[256]; + int status = getnameinfo( it->ai_addr, it->ai_addrlen, + hostBuffer, sizeof(hostBuffer), + serviceBuffer, sizeof(serviceBuffer), + NI_NAMEREQD ); + + if ( status != 0 ) { + LOG( Logger::WARNING, std::string("Could not resolve hostname. "). + append(gai_strerror(status)).c_str() ); + } else { + LOG( Logger::DEBUG, std::string("Connected to "). + append(hostBuffer).append(":"). + append(serviceBuffer).c_str() ); + } + return true; + } + + LOG( Logger::ERR, std::string("Could not connect to host," + " connection refused.").c_str() ); + return false; +} diff --git a/src/socketClient.cpp b/src/socketClient.cpp index 2cab8e1..424eba4 100644 --- a/src/socketClient.cpp +++ b/src/socketClient.cpp @@ -69,7 +69,7 @@ bool SocketClient::connect(void) return connectToPeer(); } -std::string SocketClient::errorToString(const char *s) const +std::string SocketClient::errnoToString(const char *s) { return std::string(s).append(strerror(errno)); } \ No newline at end of file diff --git a/test/test_ArgParse.hpp b/test/test_ArgParse.hpp index 1da40d0..1323044 100644 --- a/test/test_ArgParse.hpp +++ b/test/test_ArgParse.hpp @@ -8,6 +8,7 @@ #include "ArgParse.hpp" #include +#include class TestArgParseSuite : public CxxTest::TestSuite { @@ -46,7 +47,7 @@ public: TS_ASSERT_EQUALS( argParse.findKeyinArgMap("-r"), argParse.m_params.end() ); } - void asdtestAddArgs( void ) + void testAddArgs( void ) { TEST_HEADER; @@ -70,4 +71,59 @@ public: TS_ASSERT_EQUALS( argParse.foundArg("-u, --user"), true ); } + void testBadAddArgs( void ) + { + TEST_HEADER; + + ArgParse argParse("intro", "outro"); + + TS_ASSERT_THROWS( argParse.addArgument("s", "no dash"), + std::logic_error); + + TS_ASSERT_THROWS( argParse.addArgument("-i", "bad range", ArgParse::INT, + ArgParse::OPTIONAL, ArgParse::OPTIONAL, "", "12..forever"), + std::logic_error); + + TS_ASSERT_THROWS( argParse.addArgument("-i", "bad range", ArgParse::FLOAT, + ArgParse::OPTIONAL, ArgParse::OPTIONAL, "", "12.34..forever"), + std::logic_error); + } + + void testgetArgs( void ) + { + TEST_HEADER; + + ArgParse argParse("intro", "outro"); + + argParse.addArgument("-i", "int", ArgParse::INT); + argParse.addArgument("-f", "float", ArgParse::FLOAT); + argParse.addArgument("-n", "none", ArgParse::NONE); + argParse.addArgument("-s", "string", ArgParse::STRING); + argParse.addArgument("-b", "bool", ArgParse::BOOL); + + int argc = 10; + char *argv[] = { (char*)"test", + (char*)"-i", (char*)"12", + (char*)"-f", (char*)"12.12", + (char*)"-n", + (char*)"-s", (char*)"forever", + (char*)"-b", (char*)"false" }; + + argParse.parseArgs(argc, argv); + + int i; + float f; + std::string s; + bool b; + + TS_ASSERT_EQUALS( argParse.argAsInt("-i", i), true ); + TS_ASSERT_EQUALS( argParse.argAsFloat("-f", f), true ); + TS_ASSERT_EQUALS( argParse.argAsString("-s", s), true ); + TS_ASSERT_EQUALS( argParse.argAsBool("-b", b), true ); + + TS_ASSERT_EQUALS(i, 12 ); + TS_ASSERT_DELTA(f, 12.12, 0.01 ); + TS_ASSERT_EQUALS(s, std::string("forever") ); + TS_ASSERT_EQUALS(b, false ); + } };