]> gitweb.hamatoma.de Git - crepublib/commitdiff
ReTcpServer
authorhama <hama@siduction.net>
Sat, 7 Mar 2015 23:17:18 +0000 (00:17 +0100)
committerhama <hama@siduction.net>
Sat, 7 Mar 2015 23:17:18 +0000 (00:17 +0100)
13 files changed:
base/ReAppenders.cpp [new file with mode: 0644]
base/ReAppenders.hpp [new file with mode: 0644]
base/ReLogger.cpp
base/ReLogger.hpp
base/ReTestUnit.cpp
base/ReTestUnit.hpp
base/rebase.hpp
cunit/cuReLogger.cpp
cunit/cuReTCP.cpp [new file with mode: 0644]
cunit/testall.cpp
net/ReTCP.cpp
net/ReTCP.hpp
net/renet.hpp

diff --git a/base/ReAppenders.cpp b/base/ReAppenders.cpp
new file mode 100644 (file)
index 0000000..b0862ba
--- /dev/null
@@ -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 (file)
index 0000000..212a3e6
--- /dev/null
@@ -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_ */
index 7d1c1f8f5ff6d27d08f919f6b1e62e7f2655bda8..eaf997f3a7aa4d8bec46d4f3ce07445d11c6d539 100644 (file)
@@ -249,6 +249,7 @@ void ReFileAppender::changeFile() {
        m_stream = fopen(filename, "w");
        m_currentSize = 0;
 }
+
 /** @brief Constructor.
  */
 ReLogger::ReLogger(bool isGlobal) :
index f533c2f259bd2a979e9bda32f8cd21dfac7f5a87..e09086d866b795fce601e183f59aac020455ab8e 100644 (file)
@@ -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...
index 27bac118c01c2dfc0a102057603acf1c2ae4679f..bfd881c47ea456473880eab978c4da84c83ac55c 100644 (file)
@@ -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.
  *
index 86a9e532c382495e08469bf6efd4bbab78cbafa0..2752ac4c366bbcf449893db8e96e774592a23327 100644 (file)
@@ -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;
 };
index beed8a9ca7aae5f65c532e1356afa46ee7c81d1f..4e3dc9c8247e310e01d9a0551822b113e9e87c55 100644 (file)
@@ -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;
index 15e94b38ac6fa7124fa8ea835ad134c8e02741b4..22d16dfa23ac4609b2b8a638094b5312d8da83ea 100644 (file)
@@ -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 (file)
index 0000000..bd2b240
--- /dev/null
@@ -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;
+}
index 666afe400acf76e1be3aac762aea602ad509d9d0..532ef92c1c19ebb8a2eed969f0c0277d6b4dfd59 100644 (file)
@@ -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) {
index 61d20ccb83a08d58a0398937ca8f64514ad2a52d..8792a73e5b67afb40c39a78889029dcd42bc6e4c 100644 (file)
@@ -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<ReTCPServerConnection*>(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<br>
+ *                                             PS_PROCESSED: command successfully processed<br>
+ *                                             PS_FAILED: command processed, error occurred<br>
+ *                                             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;
+};
index 0f7d6a138eef53923b7230549d2becaaf91b9abe..c0d7195599ac42ff10f83af7c5fa09989f261947 100644 (file)
 /*
  * 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 ("<ip>:<port>").
+        * @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<br>
+        *                                              PS_PROCESSED: command successfully processed<br>
+        *                                              PS_FAILED: command processed, error occurred<br>
+        *                                              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_ */
index 33505cf3ada07202a0c332f5c5f10b40aa843aac..840ff532dfd1bea91a8cd20fc969b2c114c1da0b 100644 (file)
@@ -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 <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
 #include "net/ReUdpConnection.hpp"
 #include "net/ReTCP.hpp"
-
 #endif /* NET_RENET_HPP_ */