From: hama Date: Sat, 7 Mar 2015 23:17:18 +0000 (+0100) Subject: ReTcpServer X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=97a5a449f005e7a0f8a9bd229079598fc210d7a6;p=crepublib ReTcpServer --- diff --git a/base/ReAppenders.cpp b/base/ReAppenders.cpp new file mode 100644 index 0000000..b0862ba --- /dev/null +++ b/base/ReAppenders.cpp @@ -0,0 +1,41 @@ +/* + * ReAppenders.cpp + * + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib +*/ +#include "base/rebase.hpp" + +/** + * Constructor. + * + * @param maxLines if more lines are availabe, the oldest will be deleted + */ +ReMemoryAppender::ReMemoryAppender(int maxLines) : + ReAppender(), + ReSeqArray(), + m_maxLines(maxLines) +{ + setCapacity(maxLines, maxLines * 80); + // no tag, content < 2**24 Byte + setSizes(0, maxLines < 100*1000 ? 3 : 4); +} +ReMemoryAppender::~ReMemoryAppender(){ +} +/** + * Stores a logging message. + * + * @param logger the caller + * @param message the logging message to store + */ +void ReMemoryAppender::say(ReLogger* logger, const char* message){ + int theCount = count(); + if (theCount > m_maxLines) + remove(theCount - 1); + // we store in reverse order: + add(0, message != NULL ? message : logger->asCString()); +} + + diff --git a/base/ReAppenders.hpp b/base/ReAppenders.hpp new file mode 100644 index 0000000..212a3e6 --- /dev/null +++ b/base/ReAppenders.hpp @@ -0,0 +1,30 @@ +/* + * ReAppenders.hpp + * + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib + * + * These classes are separated from ReLogger.hpp because of the serialization + * of classes (or include files): + * ReLogger depends on few classes, but many classes are depending on ReLogger. +*/ + +#ifndef BASE_REAPPENDERS_HPP_ +#define BASE_REAPPENDERS_HPP_ + +/** + * Implements an appender which stores the logging messages. + */ +class ReMemoryAppender: public ReAppender, public ReSeqArray { +public: + ReMemoryAppender(int maxLines); + ~ReMemoryAppender(); +public: + virtual void say(ReLogger* logger, const char* message); +protected: + int m_maxLines; +}; + +#endif /* BASE_REAPPENDERS_HPP_ */ diff --git a/base/ReLogger.cpp b/base/ReLogger.cpp index 7d1c1f8..eaf997f 100644 --- a/base/ReLogger.cpp +++ b/base/ReLogger.cpp @@ -249,6 +249,7 @@ void ReFileAppender::changeFile() { m_stream = fopen(filename, "w"); m_currentSize = 0; } + /** @brief Constructor. */ ReLogger::ReLogger(bool isGlobal) : diff --git a/base/ReLogger.hpp b/base/ReLogger.hpp index f533c2f..e09086d 100644 --- a/base/ReLogger.hpp +++ b/base/ReLogger.hpp @@ -127,7 +127,6 @@ protected: size_t m_currentSize; size_t m_currentFileNo; }; - /** This class allows the logging of messages. * The output itself is done by so called appenders. * This allows a flexible handling of different media: files, dialog boxes, console... diff --git a/base/ReTestUnit.cpp b/base/ReTestUnit.cpp index 27bac11..bfd881c 100644 --- a/base/ReTestUnit.cpp +++ b/base/ReTestUnit.cpp @@ -20,7 +20,10 @@ ReTestUnit::ReTestUnit(const char* name, const char* sourceFile) : m_errorCount(0), m_name(name), m_sourceFile(sourceFile), - m_buffer() { + m_buffer(), + m_memoryAppender(new ReMemoryAppender(1024)), + m_silentLogger(){ + m_silentLogger.addAppender(m_memoryAppender); int ix = m_sourceFile.rindexOf(OS_SEPARATOR, 1); if (ix >= 0) m_sourceFile.remove(0, ix + 1); @@ -33,6 +36,8 @@ ReTestUnit::ReTestUnit(const char* name, const char* sourceFile) : ReTestUnit::~ReTestUnit() { if (m_errorCount > 0) logF(false, i18n("+++ %s: %d error(s)"), m_name.str(), m_errorCount); + delete m_memoryAppender; + m_memoryAppender = NULL; } /** @brief Checks a boolean expression. A false value will be logged. * diff --git a/base/ReTestUnit.hpp b/base/ReTestUnit.hpp index 86a9e53..2752ac4 100644 --- a/base/ReTestUnit.hpp +++ b/base/ReTestUnit.hpp @@ -10,6 +10,7 @@ #ifndef RETESTUNIT_H_ #define RETESTUNIT_H_ +class ReMemoryAppender; class ReTestUnit { public: ReTestUnit(const char* name, const char* sourcefile); @@ -53,6 +54,8 @@ protected: ReByteBuffer m_name; ReByteBuffer m_sourceFile; ReByteBuffer m_buffer; + ReMemoryAppender* m_memoryAppender; + ReLogger m_silentLogger; protected: static ReByteBuffer m_tempDir; }; diff --git a/base/rebase.hpp b/base/rebase.hpp index beed8a9..4e3dc9c 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -89,6 +89,7 @@ inline int getLastOSError() { #include "base/ReConfigFile.hpp" #include "base/ReI18N.hpp" #include "base/ReProgramArgs.hpp" +#include "base/ReAppenders.hpp" typedef unsigned char byte_t; typedef int ReErrNo_t; diff --git a/cunit/cuReLogger.cpp b/cunit/cuReLogger.cpp index 15e94b3..22d16df 100644 --- a/cunit/cuReLogger.cpp +++ b/cunit/cuReLogger.cpp @@ -16,6 +16,10 @@ public: } private: void run() { + testMemoryAppender(); + testBase(); + } + void testBase(){ ReStreamAppender app1(stdout); app1.setMode(CAT_ALL, CAT_ALL, CAT_ALL, GRAN_ALL); @@ -31,6 +35,22 @@ private: .end(); globalLogger()->say(CAT_LIB, __LINE__, "globalLogger()"); } + void testMemoryAppender(){ + ReMemoryAppender appender(3); + appender.say(NULL, "1"); + appender.say(NULL, "2"); + appender.say(NULL, "3"); + ReByteBuffer line; + checkEqu(3U, appender.count()); + checkEqu("1", appender.get(0, line)); + checkEqu("2", appender.get(1, line)); + checkEqu("3", appender.get(2, line)); + appender.say(NULL, "4"); + checkEqu(3U, appender.count()); + checkEqu("2", appender.get(0, line)); + checkEqu("3", appender.get(1, line)); + checkEqu("4", appender.get(2, line)); + } }; extern void testReLogger(void); diff --git a/cunit/cuReTCP.cpp b/cunit/cuReTCP.cpp new file mode 100644 index 0000000..bd2b240 --- /dev/null +++ b/cunit/cuReTCP.cpp @@ -0,0 +1,32 @@ +/* + * cuReTCP.cpp + * + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib + */ + +#include "base/rebase.hpp" +#include "net/renet.hpp" + +class TestReTCP: public ReTestUnit { +public: + TestReTCP() : + ReTestUnit("ReTCP", __FILE__) { + run(); + } +private: + void run() { + testServer(); + } + void testServer() { + ReTCPEchoServer server(58111, &m_silentLogger); + server.listenForAll(); + } +}; +extern void testReTCP(void); + +void testReTCP(void) { + TestReTCP unit; +} diff --git a/cunit/testall.cpp b/cunit/testall.cpp index 666afe4..532ef92 100644 --- a/cunit/testall.cpp +++ b/cunit/testall.cpp @@ -70,6 +70,11 @@ void testOs() { testReDirTools(); } } +void testNet() { + void testReTCP(); + testReTCP(); +} + void testMath() { extern void testReMD5(); testReMD5(); @@ -80,11 +85,12 @@ void testMath() { } void testAll() { try { - testOs(); + testNet(); if (s_testAll) { testString(); testMath(); testOs(); + testNet(); testBase(); } } catch (ReException e) { diff --git a/net/ReTCP.cpp b/net/ReTCP.cpp index 61d20cc..8792a73 100644 --- a/net/ReTCP.cpp +++ b/net/ReTCP.cpp @@ -16,97 +16,115 @@ enum LOCATION_DIRTOOL { LC_LISTEN_FOR_ALL_5, // 50505 LC_LISTEN_FOR_ALL_6, // 50506 LC_WRITE_1, // 50507 + LC_CONNECT_1, // 50508 + LC_SOCKET_ADDR_SET_1, // 50509 + LC_LISTEN_FOR_ALL_7, // 50510 + LC_HANDLE_CONNECTION_1, // 50511 }; +/** + * Constructor. + * + * @param logger the logger for error handling + */ +ReSocketAddress::ReSocketAddress(ReLogger* logger) : + m_preferredFamily(AF_INET), + m_family(-1), + // m_addr; + m_port(0), + m_logger(logger), + // m_ip + m_name() { + memset(&m_addr, 0, sizeof m_addr); + memset(&m_ip, 0, sizeof m_ip); +} +/** + * Destructor. + */ +ReSocketAddress::~ReSocketAddress() { +} /** - * Base class for TCP servers and clients. + * Sets the data from symbolic values. */ -class ReTCPConnection { -public: - ReTCPConnection(int id, ReLogger* logger); - virtual ~ReTCPConnection(); -public: - /** - * Returns the connection id. - * @return the id - */ - inline int id() const { - return m_id; - } - /** - * Returns the connection port. - * @return the port - */ - inline int port() const { - return m_port; - } - /** - * Returns the socket (ip:port). - * @return a string describing the socket - */ - const char* socketName() const{ - return m_socketName.str(); - } - void receive(ReByteBuffer& command, ReByteBuffer& message); - void send(const char* command, const char* message, int length = -1); - /** Sets the socket handle. - * @param handle the socket handle to set - */ - inline void setHandleSocket(int handle){ - m_handleSocket = handle; - } - /** Sets the id - * @param id the id to set - */ - inline void setId(int id){ - m_id = id; +void ReSocketAddress::setAddress(const char* ip, int port) { + struct addrinfo hints; + struct addrinfo* infoList; + int status; + m_port = port; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version + hints.ai_socktype = SOCK_STREAM; + + if ((status = getaddrinfo(ip, NULL, &hints, &infoList)) != 0) { + m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_SOCKET_ADDR_SET_1, + i18n("getaddrinfo($1) failed: $2")).arg(ip).arg(errno).end(); } -protected: - int m_port; - ReByteBuffer m_socketName; - ReByteBuffer m_received; - ReLogger* m_logger; - int m_handleSocket; - struct sockaddr_in m_address; - int m_id; - uint32_t m_noSent; - uint32_t m_noReceived; -}; -class ReTCPServerConnection: public ReTCPConnection { -public: - ReTCPServerConnection(int id, ReLogger* logger); - virtual ~ReTCPServerConnection(); -public: - void handleConnection(); + struct addrinfo* ptr; + bool hasIP4 = false; + bool hasIP6 = false; -}; -/** - * Implements a multithreaded TCP server. - */ -class ReTCPServer: public ReTCPConnection { -public: - ReTCPServer(ReLogger* logger, int maxConnections = 16); - virtual ~ReTCPServer(); -public: - bool listenForAll(); -private: - ReTCPServerConnection* createConnection(int id, int socket, - struct sockaddr& address); -protected: - int m_maxConnections; - int m_countConnections; - ReTCPServerConnection** m_connections; -}; + // Search for a available ip address + // if more than one are available, prefer m_preferredFamily: + m_ip[0] = '\0'; + for (ptr = infoList; ptr != NULL; ptr = ptr->ai_next) { + // different fields in IPv4 and IPv6: + if (!hasIP4 && ptr->ai_family == AF_INET) { + m_family = AF_INET; + m_addr.m_addr4 = *(struct sockaddr_in *) ptr->ai_addr; + hasIP4 = true; + inet_ntop(ptr->ai_family, &m_addr.m_addr4.sin_addr, m_ip, + sizeof m_ip); + } else if (!hasIP6 && ptr->ai_family == AF_INET6) { + m_family = AF_INET6; + m_addr.m_addr6 = *(struct sockaddr_in6 *) ptr->ai_addr; + hasIP6 = true; + inet_ntop(ptr->ai_family, &m_addr.m_addr6.sin6_addr, m_ip, + sizeof m_ip); + } + if (ptr->ai_family == m_preferredFamily) + break; + } + // free the linked list + freeaddrinfo(infoList); + if (m_ip[0] == '\0') + m_name.setLength(0); + else + m_name.set(m_ip).appendChar(':').appendInt(port); +} /** - * Implements a TCP client. + * Connects a client with the server. + * + * @param ip domain address ("denic.de") or ip address ("192.168.2.1") + * @param port port number: 1..65535 */ -class ReTCPClient: public ReTCPConnection { -public: - ReTCPClient(ReLogger* logger); - virtual ~ReTCPClient(); -}; +bool ReTCPClient::connect(const char* ip, int port) { + bool rc = false; + struct hostent* peer; + int length = sizeof(struct in_addr); + // domain name (or numerical address)? + if (isdigit(ip[0]) || strchr(ip, ':') != NULL) { + peer = gethostbyaddr(ip, length, AF_INET); + } + if (peer == NULL) { + peer = gethostbyname(ip); + } + if (peer == NULL) { + peer = gethostbyaddr(ip, length, AF_INET); + } + if (peer == NULL) { + m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_CONNECT_1, + i18n("Id=$1: cannot write ($1): $2")); + } else { + struct in_addr** addr_list; + m_peerName.setLength(0); + addr_list = (struct in_addr **) peer->h_addr_list; + for (int ii = 0; addr_list[ii] != NULL; ii++) { + m_peerName.append((const char*) inet_ntoa(*addr_list[ii]), -1); + } + } +} /** * Constructor. @@ -115,16 +133,14 @@ public: * @param logger the logger for error handling */ ReTCPConnection::ReTCPConnection(int id, ReLogger* logger) : - m_port(0), - m_socketName(), + ReSocketAddress(logger), + m_peerName(), m_received(), m_logger(logger), m_handleSocket(-1), - // m_address m_id(id), - m_noSent(0), - m_noReceived(0){ - memset(&m_address, 0, sizeof m_address); + m_noSent(0), + m_noReceived(0) { } /** @@ -132,12 +148,39 @@ ReTCPConnection::ReTCPConnection(int id, ReLogger* logger) : */ ReTCPConnection::~ReTCPConnection() { } +/** + * Finishes the connection (in both directions) and frees the resouces. + */ +void ReTCPConnection::close(){ + if (m_handleSocket >= 0){ + ::close(m_handleSocket); + m_handleSocket = -1; + } +} -void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& message){ +/** + * Sets the address given by a family and a string like "192.168.0.1:22". + * + * @param family AF_INET or AF_INET6 + * @param ip the string describing the address, e.g. "192.168.0.1" + * @param port the port of the peer + */ +void ReTCPConnection::setConnectedAddress(int family, const char* ip, int port){ + m_family = family; + m_name = ip; + m_port = port; + int length = strlen(ip); + memcpy(m_ip, ip, length); + m_ip[length] = '\0'; + m_name.appendChar(':').appendInt(port); +} +void ReTCPConnection::receive(ReByteBuffer& command, ReByteBuffer& message) { + command.setLength(8); + int received = recv(m_handleSocket, command.buffer(), 8, 0); } void ReTCPConnection::send(const char* command, const char* message, - int length){ + int length) { if (length < 0) length = strlen(message); ReByteBuffer header; @@ -153,12 +196,12 @@ void ReTCPConnection::send(const char* command, const char* message, error = error + 0; #elif defined __WIN32__ #endif - if (error != 0){ + if (error != 0) { error += 0; m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_WRITE_1, - i18n("Id=$1: cannot write ($1): $2")); + i18n("Id=$1: cannot write ($1): $2")); //.arg.(m_id) - // .arg(error).arg(m_socketName).end(); + // .arg(error).arg(m_peerName).end(); } } @@ -168,27 +211,57 @@ void ReTCPConnection::send(const char* command, const char* message, * @param id an identifier for logging * @param logger the logger for error handling */ -ReTCPServerConnection::ReTCPServerConnection(int id, ReLogger* logger) : - ReTCPConnection(id, logger) { +ReTCPServerConnection::ReTCPServerConnection(int id, ReLogger* logger, + ReTCPServer* server) : + ReTCPConnection(id, logger), + m_server(server){ } /** * Destructor. */ -ReTCPServerConnection::~ReTCPServerConnection(){ +ReTCPServerConnection::~ReTCPServerConnection() { +} + +/** + * Serves the commands of a single connection (in a single thread). + */ +void ReTCPServerConnection::handleConnection() { + //Get the socket descriptor + int read_size; + ReByteBuffer command; + ReNetCommandHandler::ProcessingState rc = ReNetCommandHandler::PS_UNDEF; + do { + receive(command, m_received); + rc = m_server->handler().handleNetCommand(command, m_received, this); + if (rc == ReNetCommandHandler::PS_UNKNOWN){ + m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_HANDLE_CONNECTION_1, + i18n("unknown command: $1 length: $2")).arg(command) + .arg(m_received.length()).end(); + } + } while(rc != ReNetCommandHandler::PS_STOP); + close(); + m_id = -1; } /** * Constructor. * + * @param port the port for listening + * @param commandHandler a handler which can process the incoming commands. + * May be NULL * @param logger the logger for error handling * @param maxConnections maximal count of threads handling a connection */ -ReTCPServer::ReTCPServer(ReLogger* logger, int maxConnections) : +ReTCPServer::ReTCPServer(int port, class ReNetCommandHandler& commandHandler, + ReLogger* logger, int maxConnections) : ReTCPConnection(0, logger), m_maxConnections(maxConnections), m_countConnections(0), - m_connections(new ReTCPServerConnection*[maxConnections]) { + m_connections(new ReTCPServerConnection*[maxConnections]), + m_handler(commandHandler) { + m_port = port; + memset(m_connections, 0, maxConnections * sizeof *m_connections); } /** @@ -202,27 +275,39 @@ ReTCPServer::~ReTCPServer() { delete[] m_connections; m_connections = NULL; } - -ReTCPServerConnection* ReTCPServer::createConnection(int id, int socket, - struct sockaddr& address) { +/** + * Creates a server connection. + * + * @param id the connection identifier + * @param handleSocket the handle of the read/write channel + * @address the data about the client connection (ip, port) + */ +ReTCPServerConnection* ReTCPServer::createConnection(int id, + int handleSocket, const struct sockaddr& address) { ReTCPServerConnection* rc = NULL; - - for (int ii = 0; ii < m_maxConnections; ii++) { + for (int ii = 0; rc == NULL && ii < m_maxConnections; ii++) { if (m_connections[ii] == NULL) - m_connections[ii] = rc = new ReTCPServerConnection(id, m_logger); + m_connections[ii] = rc = new ReTCPServerConnection(id, m_logger, this); else if (m_connections[ii]->id() < 0) { rc = m_connections[ii]; rc->setId(id); } } if (rc != NULL) { - rc->setHandleSocket(socket); - rc->setAddress(address); + rc->setHandleSocket(handleSocket); + char ip[INET6_ADDRSTRLEN]; + inet_ntop(address.sa_family, address.sa_family == AF_INET + ? (void *) &(((struct sockaddr_in*) &address)->sin_addr) + : (void *) &(((struct sockaddr_in6*) &address)->sin6_addr), + ip, sizeof ip); + int port = address.sa_family == AF_INET + ? (int) ntohs(((struct sockaddr_in*) &address)->sin_port) + : (int) ntohl(((struct sockaddr_in6*) &address)->sin6_port); + rc->setConnectedAddress(address.sa_family, ip, port); } return rc; } - /** * The start routine of pthread_start * @@ -230,38 +315,54 @@ ReTCPServerConnection* ReTCPServer::createConnection(int id, int socket, * * @param pConnection a void* pointer to the ReTCPServerConnection instance * */ -void* connection_handler(void *pConnection) { +static void* connection_handler(void *pConnection) { ReTCPServerConnection* connection = reinterpret_cast(pConnection); connection->handleConnection(); } - /** * Accepts connections and create a thread which will handle this connection. */ bool ReTCPServer::listenForAll() { bool rc = false; - //Create socket - m_handleSocket = socket(AF_INET, SOCK_STREAM, 0); + struct addrinfo hints; + struct addrinfo* addrInfo; + + // first, load up address structs with getaddrinfo(): + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // fill in my IP for me + + memcpy(m_ip, "0.0.0.0", 8); + m_name.set(m_ip).appendChar(':').appendInt(m_port); + getaddrinfo(NULL, ReByteBuffer().appendInt(m_port).str(), &hints, + &addrInfo); + m_family = addrInfo->ai_family; + // make a socket: + m_handleSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype, + addrInfo->ai_protocol); if (m_handleSocket == -1) { m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_1, i18n("cannot create a socket: $1")).arg(errno).end(); } else { - //Prepare the sockaddr_in structure - m_address.sin_family = AF_INET; - m_address.sin_addr.s_addr = INADDR_ANY; - m_address.sin_port = htons(m_port); - - //Bind - if (bind(m_handleSocket, (struct sockaddr *) &m_address, sizeof(m_address)) - < 0) { + int yes = 1; + // Avoid the "Address already in use" error message of finished processes + // that are still waiting for the release by the kernel: + if (setsockopt(m_handleSocket, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == -1) { + m_logger->sayF(LOG_WARNING | CAT_NETWORK, LC_LISTEN_FOR_ALL_7, + i18n("setsockopt() failed: $1")).arg(errno).end(); + // this error is not fatal, continue! + } + // bind it to the port we passed in to getaddrinfo(): + if (bind(m_handleSocket, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0) m_logger->sayF(LOG_ERROR | CAT_NETWORK, LC_LISTEN_FOR_ALL_2, - i18n("cannot bind: $1")).arg( - errno).end(); - } else { + i18n("cannot bind: $1")).arg(errno).end(); + else { //Listen - listen(m_handleSocket, 3); + listen(m_handleSocket, m_maxConnections < 16 ? m_maxConnections : 16); //Accept and incoming connection m_logger->sayF(LOG_INFO | CAT_NETWORK, LC_LISTEN_FOR_ALL_3, @@ -281,7 +382,7 @@ bool ReTCPServer::listenForAll() { LC_LISTEN_FOR_ALL_5, i18n("connection refused (too many connections): $1")) .arg(m_port).end(); - close(clientSocket); + ::close(clientSocket); } else { pthread_t sniffer_thread; ReTCPConnection* connection = createConnection(nextId++, @@ -293,7 +394,8 @@ bool ReTCPServer::listenForAll() { LC_LISTEN_FOR_ALL_6, i18n("cannot create a thread: $1")).arg( getLastOSError()).end(); - close(clientSocket); + ::close(clientSocket); + clientSocket = -1; } //Now join the thread , so that we dont terminate before the thread @@ -301,7 +403,6 @@ bool ReTCPServer::listenForAll() { puts("Handler assigned"); } } - if (clientSocket < 0) { perror("accept failed"); return 1; @@ -311,35 +412,69 @@ bool ReTCPServer::listenForAll() { return rc; } -void ReTCPServerConnection::handleConnection() { - //Get the socket descriptor - int read_size; - char *message, client_message[2000]; -#if 0 - receive(m_received, command); - if (! m_received.startsWith("login")) -//Send some messages to the m_addrClient - message = "Greetings! I am your connection handler\n"; - write(m_handleSocket, message, strlen(message)); - - message = "Now type something and i shall repeat what you type \n"; - write(m_handleSocket, message, strlen(message)); +/** + * Constructor. + */ +ReNetCommandHandler::ReNetCommandHandler() : + m_nextHandler(NULL) { +} -//Receive a message from m_addrClient - while ((read_size = recv(sock, client_message, 2000, 0)) > 0) { - //Send the message back to m_addrClient - write(m_handleSocket, client_message, strlen(client_message)); - } +/** + * Adds a handler at the end of the handler chain. + */ +void ReNetCommandHandler::addHandler(ReNetCommandHandler* handler){ + if (m_nextHandler == NULL) + m_nextHandler = handler; + else + m_nextHandler->addHandler(handler); +} - if (read_size == 0) { - puts("Client disconnected"); - fflush(stdout); - } else if (read_size == -1) { - perror("recv failed"); - } -#endif -//Free the socket pointer - close(m_handleSocket); - m_id = -1; +/** + * Constructor. + * + * @param port port for listening + * @param logger logger for error handling + */ +ReTCPEchoServer::ReTCPEchoServer(int port, ReLogger* logger) : + ReTCPServer(port, *this, logger), + ReNetCommandHandler() { +} +/** + * Destructor. + */ +ReTCPEchoServer::~ReTCPEchoServer(){ } +/** + * Handler for the commands "echo", "localtim" and "stop". + * + * @param command a string describing what do to + * @param data data of the command, may be empty + * @param connection the connection which can be used for answers + * @result PS_UNKNOWN: command is not known
+ * PS_PROCESSED: command successfully processed
+ * PS_FAILED: command processed, error occurred
+ * PS_ABORT: connection should be finished + */ +ReNetCommandHandler::ProcessingState ReTCPEchoServer::handleNetCommand( + ReByteBuffer& command, ReByteBuffer& data, ReTCPConnection* connection){ + ProcessingState rc = PS_UNDEF; + if (command.equals("echo ")){ + connection->send("Echo ", data.str(), data.length()); + rc = PS_PROCESSED; + } else if (command.equals("localtim")){ + time_t now2 = time(NULL); + struct tm* now = localtime(&now2); + char buffer[128]; + strftime(buffer, sizeof buffer, "%y.%m.%d %H:%M:%S", now); + connection->send("Localtim", buffer, strlen(buffer)); + rc = PS_PROCESSED; + } else if (command.equals("stop ")){ + rc = PS_STOP; + } else { + rc = PS_UNKNOWN; + if (m_nextHandler != NULL) + rc = m_nextHandler->handleNetCommand(command, data, connection); + } + return rc; +}; diff --git a/net/ReTCP.hpp b/net/ReTCP.hpp index 0f7d6a1..c0d7195 100644 --- a/net/ReTCP.hpp +++ b/net/ReTCP.hpp @@ -1,11 +1,203 @@ /* * ReTCP.hpp * - * Created on: 04.03.2015 - * Author: hm + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib */ #ifndef NET_RETCP_HPP_ #define NET_RETCP_HPP_ +/** + * Administrates the internal data of an ip address, usable for IP4 and IP6. + */ +class ReSocketAddress { +public: + ReSocketAddress(ReLogger* logger); + virtual ~ReSocketAddress(); +public: + /** + * Returns the ip address, e.g. "192.168.2.1". + * @return a string with the ip address (IP4 or IP6) + */ + const char* ip() const { + return m_ip; + } + /** + * Returns the connection port. + * @return the port + */ + inline int port() const { + return m_port; + } + /** + * Returns the socket name (":"). + * @return a string describing the socket, e.g. "192.168.2.1:443" + */ + const char* name() const { + return m_name.str(); + } + void setAddress(const char* ip, int port); + /** Sets the preferred IP family type ( + * @param type AF_INET (for IP4) or AF_INET6 (for IP6) + */ + inline void setPreferredFamily(int family) { + m_preferredFamily = family; + } +protected: + //@ if both families are available this family will be chosen. + //@ AF_INET (for IP4) or AF_INET6 (for IP6) + int m_preferredFamily; + //@ AF_INET (for IP4) or AF_INET6 (for IP6) + int m_family; + union { + //@ IP4 variant: + struct sockaddr_in m_addr4; + //@ IP6 variant: + struct sockaddr_in6 m_addr6; + } m_addr; + int m_port; + ReLogger* m_logger; + char m_ip[INET6_ADDRSTRLEN + 1]; + ReByteBuffer m_name; +}; + +/** + * Base class for TCP servers and clients. + */ +class ReTCPConnection: public ReSocketAddress { +public: + ReTCPConnection(int id, ReLogger* logger); + virtual ~ReTCPConnection(); +public: + void close(); + /** + * Returns the connection id. + * @return the id + */ + inline int id() const { + return m_id; + } + void receive(ReByteBuffer& command, ReByteBuffer& message); + void send(const char* command, const char* message, int length = -1); + void setConnectedAddress(int family, const char* ip, int port); + /** Sets the socket handle. + * @param handle the socket handle to set + */ + inline void setHandleSocket(int handle) { + m_handleSocket = handle; + } + /** Sets the id + * @param id the id to set + */ + inline void setId(int id) { + m_id = id; + } +protected: + ReByteBuffer m_peerName; + ReByteBuffer m_received; + ReLogger* m_logger; + int m_handleSocket; + int m_id; + uint32_t m_noSent; + uint32_t m_noReceived; +}; + +/** + * Implements a TCP client. + */ +class ReTCPClient: public ReTCPConnection { +public: + ReTCPClient(ReLogger* logger); + virtual ~ReTCPClient(); +public: + bool connect(const char* ip, int port); +}; + +class ReTCPServer; +/** + * Implements a single server connection to a client (in a single thread). + */ +class ReTCPServerConnection: public ReTCPConnection { +public: + ReTCPServerConnection(int id, ReLogger* logger, ReTCPServer* server); + virtual ~ReTCPServerConnection(); +public: + void handleConnection(); +private: + ReTCPServer* m_server; +}; +class ReNetCommandHandler; +/** + * Implements a multithreaded TCP server. + */ +class ReTCPServer: public ReTCPConnection { +public: + ReTCPServer(int port, class ReNetCommandHandler& commandHandler, + ReLogger* logger, int maxConnections = 16); + virtual ~ReTCPServer(); +public: + /** Returns the command handler. + * @return the handler for the incoming messages + */ + inline ReNetCommandHandler& handler(){ + return m_handler; + } + bool listenForAll(); +private: + ReTCPServerConnection* createConnection(int id, int handleSocket, + const struct sockaddr& address); + +protected: + int m_maxConnections; + int m_countConnections; + ReTCPServerConnection** m_connections; + ReNetCommandHandler& m_handler; +}; + +/** + * An abstract base class of a handler which processes the incoming TCP commands. + */ +class ReNetCommandHandler { +public: + enum ProcessingState { + PS_UNDEF, // undefined value + PS_UNKNOWN, // command not recognized + PS_PROCESSED, // task has been successfully handled + PS_FAILED, // task has been handled, error occurred + PS_STOP, // no further processing should be done + }; +public: + ReNetCommandHandler(); + /** Destructor. + */ + virtual ~ReNetCommandHandler(){ + } +public: + void addHandler(ReNetCommandHandler* handler); + /** Processes the command from the TCP channel. + * @param command a string describing what do to + * @param data data of the command, may be empty + * @param connection the connection which can be used for answers + * @result PS_UNKNOWN: command is not known
+ * PS_PROCESSED: command successfully processed
+ * PS_FAILED: command processed, error occurred
+ * PS_ABORT: connection should be finished + */ + virtual ProcessingState handleNetCommand(ReByteBuffer& command, + ReByteBuffer& data, ReTCPConnection* connection) = 0; + +protected: + ReNetCommandHandler* m_nextHandler; +}; +class ReTCPEchoServer : public ReTCPServer, public ReNetCommandHandler { +public: + ReTCPEchoServer(int port, ReLogger* logger); + virtual ~ReTCPEchoServer(); +public: + virtual ProcessingState handleNetCommand(ReByteBuffer& command, + ReByteBuffer& data, ReTCPConnection* connection); +}; #endif /* NET_RETCP_HPP_ */ diff --git a/net/renet.hpp b/net/renet.hpp index 33505cf..840ff53 100644 --- a/net/renet.hpp +++ b/net/renet.hpp @@ -1,14 +1,20 @@ /* * renet.hpp * - * Created on: 23.12.2014 - * Author: hm + * License: Public domain + * Do what you want. + * No warranties and disclaimer of any damages. + * The latest sources: https://github.com/republib + * */ #ifndef NET_RENET_HPP_ #define NET_RENET_HPP_ +#include +#include +#include +#include #include "net/ReUdpConnection.hpp" #include "net/ReTCP.hpp" - #endif /* NET_RENET_HPP_ */