]> gitweb.hamatoma.de Git - reqt/commitdiff
dayly work
authorJ. Hamatoma <hama@siduction.net>
Sat, 14 Dec 2013 23:54:44 +0000 (00:54 +0100)
committerJ. Hamatoma <hama@siduction.net>
Sat, 14 Dec 2013 23:54:44 +0000 (00:54 +0100)
19 files changed:
rplcore/rplcontainer.cpp
rplcore/rplcontainer.hpp
rplcore/rplcore.hpp
rplcore/rpllogger.cpp
rplcore/rpllogger.hpp
rplcore/rplterminator.cpp [new file with mode: 0644]
rplcore/rplterminator.hpp [new file with mode: 0644]
rplcore/rpltest.hpp
rplmath/rplenigma.hpp
rplmath/rplmath.hpp
rplmath/rplrandom.cpp
rplmath/rplrandom.hpp
rplnet/rplnet.hpp
rplnet/rpltcpclient.cpp [new file with mode: 0644]
rplnet/rpltcpclient.hpp [new file with mode: 0644]
rplnet/rpltcppeer.cpp [new file with mode: 0644]
rplnet/rpltcppeer.hpp [new file with mode: 0644]
rplnet/rpltcpserver.cpp
rplnet/rpltcpserver.hpp

index 875570c60c3872b93250f146fd5d5401c41431c5..afe61eaae4af8765321d31b9436a6bda86bf5e38 100644 (file)
@@ -63,6 +63,13 @@ RplContainer::RplContainer(size_t sizeHint) :
     m_readPosition(NULL)
 {
 }
+
+/**
+ * @brief Destructor.
+ */
+RplContainer::~RplContainer(){
+}
+
 /**
  * @brief Adds an type to the type list.
  *
index 1289f4c50d8c59ad40ab6e7b00dd2c4b86e7d1c0..99bc3bc25de34cefd1cce1efa690f6ae1225a313 100644 (file)
@@ -29,6 +29,12 @@ public:
     static const char* MAGIC_1;
 public:
     RplContainer(size_t sizeHint);
+    virtual ~RplContainer();
+private:
+    // No copy constructor: no implementation!
+    RplContainer(const RplContainer& source);
+    // Prohibits assignment operator: no implementation!
+    RplContainer& operator =(const RplContainer& source);
 public:
     // Building the container:
     void addType(type_tag_t tag);
index e966f34fdfc9a0025befc89864a8b52c243cc5de..29383941d8c60f82add5f1bee5485acaea25d455 100644 (file)
@@ -33,5 +33,6 @@ typedef unsigned char uint8_t;
 #include "rplcontainer.hpp"
 #include "rplstring.hpp"
 #include "rplconfig.hpp"
+#include "rplterminator.hpp"
 
 #endif // RPLCORE_HPP
index 6d6f20a0ca9bf8c4d9f2be21842fbdcfbabba836..d3181aa27e62ecfca607ccd37f67ea0bb68ccc20 100644 (file)
@@ -181,6 +181,36 @@ char RplLogger::getPrefixOfLevel(RplLoggerLevel level) const {
     }
     return rc;
 }
+
+/**
+ * @brief Tests whether at least one appender is active for a given level.
+ *
+ * @param level     level to test
+ * @return          false: all appenders are not activated by this level<br>
+ *                  true: otherwise
+ */
+bool RplLogger::isActive(RplLoggerLevel level) const{
+    bool rc = false;
+    for (size_t ix = 0; ! rc && ix < m_countAppenders; ix++){
+        RplAppender* appender = m_appenders[ix];
+        if (appender->isActive(level))
+            rc = true;
+    }
+    return rc;
+}
+
+/**
+ * @brief Sets the log level for all appenders.
+ *
+ * @param level     level to set
+ */
+void RplLogger::setLevel(RplLoggerLevel level){
+    for (size_t ix = 0; ix < m_countAppenders; ix++){
+        RplAppender* appender = m_appenders[ix];
+        appender->setLevel(level);
+    }
+}
+
 /**
  * @brief Returns the standard prefix of a logging line.
  *
index c01d94e5a3cb05dda75714811d71794b2f1cab10..3c0af244cff71475ce25edf17ee15e040c9ca8c7 100644 (file)
@@ -32,6 +32,12 @@ class RplAppender
 public:
     RplAppender(const QByteArray& name);
     virtual ~RplAppender();
+private:
+    // No copy constructor: no implementation!
+    RplAppender(const RplAppender& source);
+    // Prohibits assignment operator: no implementation!
+    RplAppender& operator =(const RplAppender& source);
+public:
     virtual void log(RplLoggerLevel level, int location, const char* message,
         RplLogger* logger) = 0;
     bool isActive(RplLoggerLevel level);
@@ -61,6 +67,11 @@ private:
 public:
     RplLogger();
     virtual ~RplLogger();
+private:
+    // No copy constructor: no implementation!
+    RplLogger(const RplLogger& source);
+    // Prohibits assignment operator: no implementation!
+    RplLogger& operator =(const RplLogger& source);
 public:
     bool log(RplLoggerLevel level, int location, const char* message);
     bool log(RplLoggerLevel level, int location, const QByteArray& message);
@@ -74,6 +85,8 @@ public:
     QByteArray buildStdPrefix(RplLoggerLevel level, int location);
     const QByteArray& getStdPrefix(RplLoggerLevel level, int location);
     char getPrefixOfLevel(RplLoggerLevel level) const;
+    bool isActive(RplLoggerLevel level) const;
+    void setLevel(RplLoggerLevel level);
 private:
     // the assigned appenders:
     RplAppender* m_appenders[16];
diff --git a/rplcore/rplterminator.cpp b/rplcore/rplterminator.cpp
new file mode 100644 (file)
index 0000000..41dd99f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#include "rplcore.hpp"
+
+enum {
+    LOC_CAUSE_TERMINATION_1 = RPL_FIRST_OF(RPLMODULE_TERMINATOR), // 901
+};
+
+/**
+ * @class RplTerminator rplterminator.hpp "rplcore/rplterminator.hpp"
+ *
+ * @brief Implements a thread stopper.
+ *
+ * Allows to terminate a thread avoiding unfreeing resources, deadlocks etc.
+ *
+ * The application must create one instance of a <code>RplTerminator</code>.
+ * All threads gets this instance and call them periodically if the application should stop.
+ * If yes they finish their work, frees the resources and stop.
+ *
+ */
+
+/**
+ * @brief Constructor.
+ * @param logger    NULL or the logger. Used to protocol the termination
+ */
+RplTerminator::RplTerminator(RplLogger* logger) :
+    m_stop(false),
+    m_logger(logger)
+{
+}
+
+/**
+ * @brief Destructor.
+ */
+RplTerminator::~RplTerminator(){
+}
+
+/**
+ * @brief Defines the stop of all threads.
+ *
+ * @param reason    the reason of the termination. Will be logged (if a logger is defined)
+ * @param file      NULL or the file of the caller. Normally set with <code>__FILE__</code>
+ * @param lineNo    0 or the line number of the caller. Normally set with <code>__LINE__</code>
+ * @param level     log level
+ * @param location  0 or the location of the caller
+ */
+void RplTerminator::causeTermination(const char* reason, const char* file,
+        int lineNo, RplLoggerLevel level, int location){
+    if (m_logger != NULL){
+        QByteArray message(reason);
+        if (file != NULL){
+            message.append(" [").append(file).append(lineNo).append("]");
+        }
+        m_logger->log(level, location == 0 ? LOC_CAUSE_TERMINATION_1 : location,
+                      message);
+    }
+    m_stop = true;
+}
+
+/**
+ * @brief Tests whether the thread should be stopped.
+ * @return  true: the thread should be stopped.<br>
+ *          false: otherwise
+ */
+bool RplTerminator::isStopped() const{
+    return m_stop;
+}
+
diff --git a/rplcore/rplterminator.hpp b/rplcore/rplterminator.hpp
new file mode 100644 (file)
index 0000000..25e5fca
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#ifndef RPLTERMINATOR_HPP
+#define RPLTERMINATOR_HPP
+
+class RplTerminator
+{
+public:
+    RplTerminator(RplLogger* logger = NULL);
+    virtual ~RplTerminator();
+private:
+    // No copy constructor: no implementation!
+    RplTerminator(const RplTerminator& source);
+    // Prohibits assignment operator: no implementation!
+    RplTerminator& operator =(const RplTerminator& source);
+public:
+    void causeTermination(const char* reason, const char* file = NULL,
+        int lineNo = 0, RplLoggerLevel level = LOG_ERROR, int location = 0);
+    bool isStopped() const;
+private:
+    bool m_stop;
+    RplLogger* m_logger;
+};
+
+#endif // RPLTERMINATOR_HPP
index 95fc248871699a2908bca82bb256e69e800e40cb..142459f878690a69073746562f5703a49efde111 100644 (file)
@@ -18,6 +18,11 @@ class RplTest
 public:
     RplTest(const char* name);
     virtual ~RplTest();
+private:
+    // No copy constructor: no implementation!
+    RplTest(const RplTest& source);
+    // Prohibits assignment operator: no implementation!
+    RplTest& operator =(const RplTest& source);
 public:
     bool assertEquals(int expected, int current, const char* file, int lineNo);
     bool assertEquals(const char* expected, const char* current,
index 0b7709f5641c1e168e94c44c79200a71eae0e5a5..e2ed1d376130f227441bca150480efa9df300142 100644 (file)
@@ -28,6 +28,11 @@ protected:
 public:
     RplEnigma(RplRandom* random = NULL);
     virtual ~RplEnigma();
+private:
+    // No copy constructor: no implementation!
+    RplEnigma(const RplEnigma& source);
+    // Prohibits assignment operator: no implementation!
+    RplEnigma& operator =(const RplEnigma& source);
 public:
     void encode(char* data, int size, const char* charSet, QByteArray& cache);
     void decode(char* data, int size, const char* charSet, QByteArray& cache);
index a3702d902c04cb127bd918ec871a09427091017e..8613802dcbaf67a1ab59d0233667bf136ae1bd44 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef RPLMATH_HPP
 #define RPLMATH_HPP
 
-#ifndef RPLCORE_HPPP
+#ifndef RPLCORE_HPP
 #include "rplcore/rplcore.hpp"
 #endif
 #include "rplrandom.hpp"
index 390c1e93ac1fa549aec774010a4e92b96c7b9c91..dc5eb904f206efe4e27e5c658440956fe3904800 100644 (file)
@@ -59,10 +59,10 @@ void RplRandom::xorSeed(u_int64_t seed){
  *
  * @return          a pseudo random value 0..255
  */
-u_int8_t RplRandom::nextByte(){
+quint8 RplRandom::nextByte(){
     u_int64_t value = nextInt64();
     // forget the last 3 bits:
-    u_int8_t rc = (u_int8_t) (value >> 3) % 256;
+    quint8 rc = (quint8) (value >> 3) % 256;
     return rc;
 }
 
index aa084667648165527a1303989d15c19f95f00786..e90a020dd460ea56eec7611ee2a843d77130556a 100644 (file)
@@ -7,11 +7,16 @@ class RplRandom
 public:
     RplRandom();
     virtual ~RplRandom();
+private:
+    // No copy constructor: no implementation!
+    RplRandom(const RplRandom& source);
+    // Prohibits assignment operator: no implementation!
+    RplRandom& operator =(const RplRandom& source);
 public:
     virtual u_int64_t nextInt64();
     virtual void setSeed(u_int64_t seed);
     void xorSeed(u_int64_t seed);
-    u_int8_t nextByte();
+    quint8 nextByte();
     int nextInt(int minValue, int maxValue);
     QByteArray nextString(int length = 8, char minChar = ' ', char maxChar = 127);
     QByteArray nextString(int length, char* charSet);
index a7eecd0c13849c51797a953c4f4d8a27cc8a1e84..2f2fe11e50f324176f49825865918ef50b72c254 100644 (file)
@@ -5,15 +5,19 @@
  * You also can use the licence from http://www.wtfpl.net/.
  * The original sources can be found on https://github.com/republib.
  */
-#ifndef RPLUTIL_HPP
-#define RPLUTIL_HPP
+#ifndef RPLNET_HPP
+#define RPLNET_HPP
 
 #include <QThread>
 #include <QAbstractSocket>
 #include <QTcpSocket>
-
 #include <QTcpServer>
 #include <QThread>
 
+//includes implicite rplcore.hpp:
+#include "../rplmath/rplmath.hpp"
+#include "rpltcppeer.hpp"
 #include "rpltcpserver.hpp"
-#endif // RPLUTIL_HPP
+#include "rpltcpclient.hpp"
+
+#endif // RPLNET_HPP
diff --git a/rplnet/rpltcpclient.cpp b/rplnet/rpltcpclient.cpp
new file mode 100644 (file)
index 0000000..adbb54b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#include "rplnet.hpp"
+
+enum {
+    LOC_1 = RPL_FIRST_OF(RPLMODULE_TCPCLIENT), // 701
+};
+
+/** @class RplTcpClient rpltcpclient.hpp "rplnet/rpltcpclient.hpp"
+ *
+ * Implements a TCP client.
+ *
+ * Use the protocol defined at <code>RplTcpServer</code>.
+ */
+/**
+ * @brief Constructor.
+ *
+ * @param ip            server ip
+ * @param port          server port
+ * @param thread        current thread. Used for <code>sleep()</code>
+ * @param terminator    NULL or for controlled termination
+ * @param logger        a logger
+ */
+RplTcpClient::RplTcpClient(const char* ip, int port, QThread* thread,
+        RplTerminator* terminator, RplLogger* logger) :
+    m_peer(new RplTcpPeer(ip, port, thread, terminator, logger))
+{
+    if (ip != NULL && port != 0)
+        setRemoteAddress(ip, port);
+}
+
+/**
+ * @brief Destructor.
+ */
+RplTcpClient::~RplTcpClient(){
+    delete m_peer;
+    m_peer = NULL;
+}
+
+void RplTcpClient::setRemoteAddress(const char* ip, int port){
+    QTcpSocket* socket = (QTcpSocket*) m_peer->getSocket();
+    delete socket;
+    if (ip == NULL || port == 0)
+        m_peer->setSocket(NULL);
+    else{
+        socket = new QTcpSocket();
+        m_peer->setSocket(socket);
+        socket->connectToHost(QString(ip), port);
+    }
+}
+
+/**
+ * @brief Returns the peer info.
+ * @return the peer info
+ */
+RplTcpPeer* RplTcpClient::getPeer() const{
+    return m_peer;
+}
+
+/** @class RplClientThread rpltcpclient.hpp "rplnet/rpltcpclient.hpp"
+ *
+ * @brief Implements a thread usable for a tcp client.
+ *
+ * Each <code>RplTcpPeer</code> needs a thread. Therefore this class provides all things
+ * needed for a <code>RplTcpClient</code> which uses a <code>RplTcpPeer</code>.
+ */
+
+/**
+ * @brief Constructor.
+ *
+ * @param ip        NULL or the ip of the server, e.g. "localhost" or "127.0.0.1"
+ * @param port      0 or the port of the server
+ * @param logger    the logger. If NULL a default logger will be used
+ */
+RplClientThread::RplClientThread(const char* ip, int port, RplLogger* logger) :
+    m_client(NULL),
+    m_logger(logger),
+    m_ownsLogger(logger == NULL)
+{
+    if (logger == NULL){
+        m_logger = new RplLogger();
+        m_logger->buildStandardAppender("client");
+        m_logger->setLevel(LOG_DEBUG);
+    }
+    m_client = new RplTcpClient(ip, port, this, NULL, logger);
+}
+/**
+ * @brief Destructor.
+ */
+RplClientThread::~RplClientThread(){
+    delete m_client;
+    m_client = NULL;
+    if (m_ownsLogger){
+        delete m_logger;
+        m_logger = NULL;
+    }
+}
+/**
+ * @brief Returns the peer which can be used for sending and receiving messages.
+ *
+ * @return the peer
+ */
+RplTcpPeer* RplClientThread::getPeer() const{
+    return m_client->getPeer();
+}
+
+/**
+ * @brief Returns the logger of the thread.
+ * @return the logger
+ */
+RplLogger* RplClientThread::getLogger() const{
+    return m_logger;
+}
+
+/**
+ * @brief Contains the main method of the thread.
+ *
+ * Calls <code>doIt()</code> for the real things.
+ */
+void RplClientThread::run(){
+    doIt();
+}
diff --git a/rplnet/rpltcpclient.hpp b/rplnet/rpltcpclient.hpp
new file mode 100644 (file)
index 0000000..0f11817
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#ifndef RPLTCPCLIENT_HPP
+#define RPLTCPCLIENT_HPP
+
+class RplTcpClient
+{
+public:
+    RplTcpClient(const char* ip, int port, QThread* thread,
+        RplTerminator* terminator, RplLogger* logger = NULL);
+    virtual ~RplTcpClient();
+private:
+    // No copy constructor: no implementation!
+    RplTcpClient(const RplTcpClient& source);
+    // Prohibits assignment operator: no implementation!
+    RplTcpClient& operator =(const RplTcpClient& source);
+public:
+    RplTcpPeer* getPeer() const;
+private:
+    void setRemoteAddress(const char* ip, int port);
+private:
+    RplTcpPeer* m_peer;
+};
+
+class RplClientThread : public QThread {
+public:
+    RplClientThread(const char* ip, int port, RplLogger* logger = NULL);
+    virtual ~RplClientThread();
+private:
+    // No copy constructor: no implementation!
+    RplClientThread(const RplClientThread& source);
+    // Prohibits the assignment operator. Not implemented!
+    RplClientThread& operator=(const RplClientThread& source);
+public:
+    /**
+     * @brief Does the main task of the thread.
+     *
+     * Will be called from <code>QThread::run()</code>.
+     * The implementations of this abstract method should be call <code>getPeer()</code>
+     * to send and receive messages.
+     */
+    virtual void doIt() = 0;
+    RplTcpPeer* getPeer() const;
+    RplLogger* getLogger() const;
+private:
+    virtual void run();
+protected:
+    RplTcpClient* m_client;
+    RplLogger* m_logger;
+private:
+    bool m_ownsLogger;
+};
+
+#endif // RPLTCPCLIENT_HPP
diff --git a/rplnet/rpltcppeer.cpp b/rplnet/rpltcppeer.cpp
new file mode 100644 (file)
index 0000000..e1e3edb
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#include "rplnet.hpp"
+
+enum {
+    LOC_SEND_1 = RPL_FIRST_OF(RPLMODULE_TCPPEER), // 801
+    LOC_READ_BYTES_1,
+    LOC_READ_BYTES_2,
+    LOC_READ_BYTES_3,
+};
+
+static int s_dummy = 0;
+
+/** @class RplTcpNode rpltcpnode.hpp "rplnet/rpltcpnode.hpp"
+ *
+ * @brief Implement the common things for TCP server and client.
+ *
+ * The communication is done with the following protocol:
+ * <ul>
+ *  <li>The data is transmitted via TCP.</li>
+ *  <li>The data exchange is done with <b>info units</b>.
+ *  <li>Each info unit contains a header and the data.</li>
+ * </ul>
+ * The format of the header:
+ *<pre>FLAGS [SALT] COMMAND SIZE
+ * </pre>
+ * <ul>
+ *  <li>FLAGS (1 byte): a XOR sum of the flags defined in <code>RplTcpNode::flag_t.</li>
+ *  <li>SALT (4 byte): a random value. Controls the encryption. Only available if <code>FLAG_ENCRYPT</code> is set.</li>
+ *  <li>COMMAND (5 byte): define the task to do (client to server) or the answer (server to client).
+ *  <li>SIZE (2 or 4 byte): the size of the data behind the header. 4 bytes if <code>FLAG_4_BYTE_SIZE</code> is set.</li>
+ * </ul>
+ *
+ */
+
+/**
+ * @brief Creates an instance of a <code>RplTcpPeer</code>.
+ *
+ * @param ip        NULL or the ip address, e.g. "localhost" or "192.168.2.1"
+ * @param port      0 or port
+ * @param thread    the current thread. Used for sleep()
+ * @param terminator    NULL or for controlled thread termination
+ * @param logger    logger. If Null the global logger will be taken (not thread safe!)
+ * @return          a instance of <code>RplTcpPeer</code>
+ */
+RplTcpPeer* RplTcpPeer::createPeer(const char* ip, int port, QThread* thread,
+        RplTerminator* terminator, RplLogger* logger){
+    return new RplTcpPeer(ip, port, thread, terminator, logger);
+}
+
+/**
+ * @brief RplTcpNode::RplTcpNode
+ *
+ * @param ip            NULL or the ip address, e.g. "localhost" or "192.168.2.1"
+ * @param port          0 or port
+ * @param thread        the current thread. Used for sleep()
+ * @param terminator    NULL or for controlled thread termination
+ * @param logger        logger. If Null the global logger will be taken (not thread safe!)
+ */
+RplTcpPeer::RplTcpPeer(const char* ip, int port, QThread* thread,
+        RplTerminator* terminator, RplLogger* logger) :
+    m_ip(ip),
+    m_port(port),
+    m_socket(NULL),
+    m_logger(logger == NULL ? RplLogger::globalLogger() : logger),
+    m_thread(thread),
+    m_random(),
+    m_terminator(terminator)
+{
+    // Simulate a true random with time, and addresses from stack and code segment:
+    m_random.setSeed(time(NULL) + ((qint64) this << 8) + ((qint64) &s_dummy << 16) + ((qint64) &createPeer << 24));
+}
+
+/**
+ * @brief Destructor.
+ */
+RplTcpPeer::~RplTcpPeer(){
+
+}
+
+/**
+ * @brief Sends a message via TCP.
+ *
+ * @param flags     a sum of FLAGS_... constants
+ * @param command   defines the content of the message
+ * @param data      NULL or additional data
+ * @return          true: success<br>
+ *                  false: error occurred
+ */
+bool RplTcpPeer::send(qint8 flags, const char* command, const QByteArray& data){
+    bool rc = false;
+    QByteArray header;
+    header.reserve(16);
+    header.append((char) flags);
+    if (flags & FLAG_ENCRYPT){
+        header.append((char) m_random.nextByte());
+        header.append((char) m_random.nextByte());
+        header.append((char) m_random.nextByte());
+        header.append((char) m_random.nextByte());
+    }
+    unsigned int length = data.length();
+    header.append(char (length % 256));
+    header.append(char ((length >> 8) % 256));
+    if (flags & FLAG_4_BYTE_SIZE){
+        header.append(char ((length >> 16) % 256));
+        header.append(char ((length >> 24) % 256));
+    }
+    length = strlen(command);
+    header.append(command, length < 5 ? length : 5);
+    while (length++ < 5){
+        header.append(' ');
+    }
+    qint64 written = m_socket->write(header.constData(), header.length());
+    qint64 written2 = m_socket->write(data);
+    int count = 0;
+    if (written != header.length() || written2 != data.length()){
+        int endTime = time(NULL) + m_timeout;
+        // wait until the data are sent or timeout or external termination:
+        while(m_socket->bytesToWrite() > 0){
+            m_thread->msleep(1);
+            if (++count % 20 == 0){
+                if (m_terminator == NULL || m_terminator->isStopped()
+                        || time(NULL) > endTime)
+                    break;
+            }
+        }
+    }
+    if (m_logger->isActive(LOG_DEBUG))
+        m_logger->logv(LOG_DEBUG, LOC_SEND_1, "send %s-%d: %s len=%d loops=%d %s",
+                   m_ip.constData(), m_port, command, data.length(), count,
+                       RplString::hexDump((const void*) data.constData(), 16, 16).constData());
+    return rc;
+}
+
+/**
+ * @brief Reads an amount of bytes with a timeout.
+ *
+ * @param bytes     count of bytes to read
+ * @param maxTime   IN/OUT: last time the read must be ready
+ * @param loops     IN/OUT: number of sleeps
+ *
+ * @return          "": read not successful: timeout or termination or error<br>
+ *                  otherwise: the read bytes
+ */
+QByteArray RplTcpPeer::readBytes(int bytes, time_t maxTime, int& loops){
+    QAbstractSocket* socket = getSocket();
+    bool success = true;
+    qint64 bytes64 = bytes;
+    while(socket->bytesAvailable() < bytes64){
+        if (loops == 0)
+            maxTime = time(NULL) + m_timeout;
+        if (++loops % 20 == 0){
+            if (time(NULL) > maxTime){
+                m_logger->logv(LOG_ERROR, LOC_READ_BYTES_1, "receive: timeout (%d)", m_timeout);
+                success = false;
+                break;
+            } else if (m_terminator != NULL && m_terminator->isStopped()){
+                m_logger->log(LOG_ERROR, LOC_READ_BYTES_2, "receive: stopped");
+                success = false;
+                break;
+            }
+        }
+    }
+    QByteArray rc;
+    if (success){
+        rc = socket->read(bytes);
+        if (rc.length() != bytes){
+             m_logger->logv(LOG_ERROR, LOC_READ_BYTES_3, "receive: too few bytes: %d of %d",
+                rc.length(), bytes);
+        }
+    }
+    return rc;
+}
+
+int getInt(const QByteArray& data, int offset, int size){
+    int rc = ((int)(unsigned char) data.at(offset++));
+    while (--size > 0){
+        rc = rc * 256 + (unsigned char) data.at(offset++);
+    }
+    return rc;
+}
+
+/**
+ * @brief Receives a message via TCP.
+ *
+ * @param command   OUT: defines the content of the read message
+ * @param data      OUT: "" or additional data
+ * @return          true: success<br>
+ *                  false: error occurred
+ */
+bool RplTcpPeer::receive(QByteArray& command, QByteArray& data){
+    bool rc = true;
+    command.clear();
+    data.clear();
+    QByteArray header;
+    header.reserve(16);
+    int minHeaderSize = 8;
+    int loops = 0;
+    time_t maxTime = 0;
+    quint8 flags = 0;
+    header = readBytes(minHeaderSize, maxTime, loops);
+    if (header.length() > 0){
+        flags = header.at(0);
+        int headerSize = minHeaderSize;
+        if ((flags & FLAG_4_BYTE_SIZE) != 0)
+            headerSize += 2;
+        if ((flags & FLAG_ENCRYPT) != 0)
+            headerSize += 4;
+        if (headerSize != minHeaderSize){
+            QByteArray restHeader = readBytes(headerSize - minHeaderSize, maxTime, loops);
+            if (restHeader.length() == 0)
+                header.clear();
+            else
+                header.append(restHeader);
+        }
+    }
+    rc = header.length() > 0;
+    if (rc){
+        int offset = (flags & FLAG_ENCRYPT) == 0 ? 6 : 8;
+        int size = (flags & FLAG_4_BYTE_SIZE) == 0 ? 4 : 2;
+        int dataLength = getInt(header, offset, size);
+        command = header.mid(offset - 5, 5);
+        data = readBytes(dataLength, maxTime, loops);
+        rc = data.length() == dataLength;
+    }
+    return rc;
+
+}
+
+/**
+ * @brief Sends a message and receives an answer message via TCP.
+ *
+ * @param flags     a sum of FLAGS_... constants
+ * @param command       defines the content of the message to send
+ * @param data          NULL or additional data
+ * @param size          0 or the size of <code>data</code>
+ * @param answer        OUT: the command of the answer
+ * @param answerData    OUT: "" or additional data of the answer
+ * @return              true: success<br>
+ *                      false: error occurred
+ */
+bool RplTcpPeer::sendAndReceive(quint8 flags, char command [4],
+        QByteArray* data, QByteArray& answer, QByteArray& answerData){
+    answer.clear();
+    answerData.clear();
+    bool rc = send(flags, command, data == NULL ? QByteArray("") : *data);
+    if (rc)
+        rc = receive(answer, answerData);
+    return rc;
+}
+
+/**
+ * @brief Sets the socket.
+ *
+ * @param socket    the socket to set
+ */
+void RplTcpPeer::setSocket(QAbstractSocket* socket){
+    m_socket = socket;
+    //if (socket != NULL)
+        //connect( m_socket, SIGNAL(readyRead()), SLOT(readTcpData()) );
+}
+
+void RplTcpPeer::readTcpData(){
+
+}
+
+/**
+ * @brief Returns the socket.
+ *
+ * @return the socket
+ */
+QAbstractSocket* RplTcpPeer::getSocket() const{
+    return m_socket;
+}
diff --git a/rplnet/rpltcppeer.hpp b/rplnet/rpltcppeer.hpp
new file mode 100644 (file)
index 0000000..6b36acd
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Licence:
+ * You can use and modify this file without any restriction.
+ * There is no warranty.
+ * You also can use the licence from http://www.wtfpl.net/.
+ * The original sources can be found on https://github.com/republib.
+ */
+#ifndef RPLTCPPEER_HPP
+#define RPLTCPPEER_HPP
+
+#ifndef RPLNET_HPP
+#include "rplnet.hpp"
+#endif
+
+class RplTcpPeer : public QObject
+{
+    Q_OBJECT
+public:
+    enum {
+        ///> standard behaviour
+        FLAG_UNDEF,
+        ///> if set: the size field is 4 byte (max. 4 GByte) instead of 2 byte (64kByte)
+        FLAG_4_BYTE_SIZE = 1,
+        ///> if set the data are compressed by the gzip algorithm
+        FLAG_GZIP = 2,
+        ///> if set the data are encrypted. In front of the size field a 4 byte salt field exists.
+        ///> In this case all data behind the salt field are encrypted.
+        FLAG_ENCRYPT = 4,
+        ///> connection initialization of
+    } flag_t;
+public:
+    static RplTcpPeer* createPeer(const char* ip, int port, QThread* thread,
+        RplTerminator* terminator, RplLogger* logger = NULL);
+public:
+    RplTcpPeer(const char* ip, int port, QThread* thread, RplTerminator* terminator, RplLogger* logger = NULL);
+    virtual ~RplTcpPeer();
+private:
+    // No copy constructor: no implementation!
+    RplTcpPeer(const RplTcpPeer& source);
+    // No assignment operator: no implementation!
+    RplTcpPeer& operator =(const RplTcpPeer& source);
+public:
+    virtual bool send(qint8 flags, const char* command, const QByteArray& data);
+    virtual bool receive(QByteArray& command, QByteArray& data);
+    virtual bool sendAndReceive(quint8 flags, char command [4],
+        QByteArray* data, QByteArray& answer, QByteArray& answerData);
+    void setSocket(QAbstractSocket* socket);
+    QAbstractSocket* getSocket() const;
+private:
+    QByteArray readBytes(int bytes, time_t maxTime, int& loops);
+
+public:
+    void readTcpData();
+
+private:
+    QByteArray m_ip;
+    int m_port;
+    QAbstractSocket* m_socket;
+    RplLogger* m_logger;
+    QByteArray m_received;
+    int m_expected;
+    QThread* m_thread;
+    // Only used for salt generation:
+    RplRandom m_random;
+    ///> true: the read/write operation is done
+    bool m_ready;
+    ///> maximum allowed time for sending/receiving one message
+    int m_timeout;
+    ///> for controlled termination
+    RplTerminator* m_terminator;
+};
+
+#endif // RPLTCPPEER_HPP
index d2a190425b59750c30bf5b7ec687e9e30fb5cf8a..1ace63eb6a45676df25723fb9f1271b5ea391bd1 100644 (file)
@@ -7,6 +7,10 @@
  */
 #include "rplnet.hpp"
 
+enum {
+    LOC_1 = RPL_FIRST_OF(RPLMODULE_TCPSERVER), // 601
+};
+
 /** @class RplTcpThread rpltcpserver.hpp "rplcore/rpltcpserver.hpp"
  *
  * @brief Serves one connection of a multihreaded TCP server.
@@ -81,15 +85,25 @@ qintptr RplTcpThread::getSocketDescriptor() const{
  * @brief Constructor.
  *
  * @param taskHandler   this handler reads from the tcp and interprets the content
+ * @param logger        NULL or logger
  * @param parent        NULL or the parent which deletes the childen
  */
-RplTcpServer::RplTcpServer(RplTaskHandler* taskHandler, QObject *parent) :
+RplTcpServer::RplTcpServer(RplTaskHandler* taskHandler, QThread* thread, RplLogger* logger, QObject *parent) :
     QTcpServer(parent),
     m_taskHandler(taskHandler),
-    m_threadId(0)
+    m_threadId(0),
+    m_peer(NULL)
 {
 }
 
+/**
+ * @brief Returns the peer info.
+ * @return the peer info
+ */
+RplTcpPeer* RplTcpServer::getPeer() const{
+    return m_peer;
+}
+
 /**
  * @brief The slot handling a new tcp connection.
  *
@@ -98,7 +112,7 @@ RplTcpServer::RplTcpServer(RplTaskHandler* taskHandler, QObject *parent) :
 void RplTcpServer::incomingConnection(qintptr socketDescriptor)
 {
     RplTcpThread *thread = createThread(socketDescriptor, ++m_threadId, m_taskHandler);
-    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+    QTcpServer::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
     thread->start();
 }
 
index a4146fe29c957622f6a944ae38564c2b111b2ec8..84e2246d7d92cf00743642aa6b1471c8d0aea20f 100644 (file)
@@ -9,29 +9,10 @@
 #define RPLTCPSERVER_HPP
 
 // the sources generated from QT include this file directly:
-#ifndef RPLCORE_HPP
-#include <string.h>
-#include <QAbstractSocket>
-#include <QThread>
-#include <QTcpServer>
-#include <QTcpSocket>
+#ifndef RPLNET_HPP
+#include "rplnet.hpp"
 #endif
 
-/**
- * @brief Portable header for a data unit.
- *
- * This header has a fix size makes it easy to process info from a socket.
- */
-class RplNetHeader{
-public:
-    RplNetHeader() {
-        memset(this, 0, sizeof *this);
-    }
-    char m_application[2];  ///< defines the task manager which is responsible to process the data unit
-    char m_command[2];      ///< specifies the meaning of the data unit
-    char m_size[8];         ///< the size of the data unit as hexadecimal number with 8 digits
-};
-
 class RplTaskHandler
 {
 public:
@@ -77,6 +58,12 @@ class RplTcpThread : public QThread {
 public:
     RplTcpThread(qintptr socketDescriptor, int threadId, RplTaskHandler* handler);
     virtual ~RplTcpThread();
+private:
+    // No copy constructor: no implementation!
+    RplTcpThread(const RplTcpThread& source);
+     // No assignment operator: no implementation!
+    RplTcpThread& operator=(const RplTcpThread& source);
+public:
     void run();
     int getThreadId() const;
     RplTaskHandler* getTaskHandler() const;
@@ -94,12 +81,19 @@ private:
     qintptr m_socketDescriptor;
 };
 
-class RplTcpServer : public QTcpServer
+class RplTcpPeer;
+class RplTcpServer : public QTcpServer, public RplTerminator
 {
     Q_OBJECT
 public:
-    explicit RplTcpServer(RplTaskHandler* taskHandler, QObject *parent = 0);
+    explicit RplTcpServer(RplTaskHandler* taskHandler, QThread* thread, RplLogger* logger = NULL, QObject *parent = 0);
+private:
+    // No copy constructor: no implementation!
+    RplTcpServer(const RplTcpServer& source);
+     // No assignment operator: no implementation!
+    RplTcpServer& operator=(const RplTcpServer& source);
 public:
+    RplTcpPeer* getPeer() const;
     bool handleTask();
     /**
      * Creates a thread derived from RplTcpThread.
@@ -113,6 +107,7 @@ protected slots:
 private:
      RplTaskHandler* m_taskHandler;
      int m_threadId;
+     RplTcpPeer* m_peer;
 };
 
 #endif // RPLTCPSERVER_HPP