Commit 99048c1cbf0e97b4906a75462ece3f7db4979387
0 parents
Initial commit. dependencies not resolved yet.
Showing
15 changed files
with
1661 additions
and
0 deletions
.gitignore
0 → 100644
CMakeLists.txt
0 → 100644
| 1 | +++ a/CMakeLists.txt | ||
| 1 | +cmake_minimum_required(VERSION 3.0) | ||
| 2 | + | ||
| 3 | +# Check to see where cmake is located. | ||
| 4 | +if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) | ||
| 5 | + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) | ||
| 6 | +elseif( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../cmake ) | ||
| 7 | + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) | ||
| 8 | +else() | ||
| 9 | + return() | ||
| 10 | +endif() | ||
| 11 | + | ||
| 12 | +# Check to see if there is versioning information available | ||
| 13 | +if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) | ||
| 14 | + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake) | ||
| 15 | + include(osdevversion) | ||
| 16 | +endif() | ||
| 17 | + | ||
| 18 | +include(projectheader) | ||
| 19 | +project_header(osdev_network) | ||
| 20 | + | ||
| 21 | +add_subdirectory(src) | ||
| 22 | +add_subdirectory(tests) | ||
| 23 | + | ||
| 24 | +# include(packaging) | ||
| 25 | +# package_component() |
README.md
0 → 100644
| 1 | +++ a/README.md |
src/CMakeLists.txt
0 → 100644
| 1 | +++ a/src/CMakeLists.txt | ||
| 1 | +cmake_minimum_required(VERSION 3.0) | ||
| 2 | +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake) | ||
| 3 | +include(projectheader) | ||
| 4 | +project_header(network) | ||
| 5 | + | ||
| 6 | +find_package( Qt5Core REQUIRED ) | ||
| 7 | +find_package( Qt5Network REQUIRED ) | ||
| 8 | + | ||
| 9 | +include_directories( SYSTEM | ||
| 10 | + ${Qt5Core_INCLUDE_DIRS} | ||
| 11 | + ${Qt5Network_INCLUDE_DIRS} | ||
| 12 | +) | ||
| 13 | + | ||
| 14 | +include(compiler) | ||
| 15 | + | ||
| 16 | +include_directories( | ||
| 17 | + ${CMAKE_CURRENT_SOURCE_DIR}/../config | ||
| 18 | + ${CMAKE_CURRENT_SOURCE_DIR}/../global | ||
| 19 | + ${CMAKE_CURRENT_SOURCE_DIR}/../logutils | ||
| 20 | + ${CMAKE_CURRENT_SOURCE_DIR}/../datatypes | ||
| 21 | +) | ||
| 22 | + | ||
| 23 | +set(SRC_LIST | ||
| 24 | + ${CMAKE_CURRENT_SOURCE_DIR}/poolmanager.cpp | ||
| 25 | + ${CMAKE_CURRENT_SOURCE_DIR}/socketcontainer.cpp | ||
| 26 | + ${CMAKE_CURRENT_SOURCE_DIR}/tcpinterface.cpp | ||
| 27 | + ${CMAKE_CURRENT_SOURCE_DIR}/tcpsocket.cpp | ||
| 28 | + | ||
| 29 | +) | ||
| 30 | + | ||
| 31 | +include(qtmoc) | ||
| 32 | +create_mocs( SRC_LIST MOC_LIST | ||
| 33 | + ${CMAKE_CURRENT_SOURCE_DIR}/poolmanager.h | ||
| 34 | + ${CMAKE_CURRENT_SOURCE_DIR}/tcpinterface.h | ||
| 35 | + ${CMAKE_CURRENT_SOURCE_DIR}/tcpsocket.h | ||
| 36 | +) | ||
| 37 | + | ||
| 38 | +link_directories( | ||
| 39 | + ${CMAKE_BINARY_DIR}/lib | ||
| 40 | +) | ||
| 41 | + | ||
| 42 | +include(library) | ||
| 43 | +add_libraries( | ||
| 44 | + ${Qt5Core_LIBRARIES} | ||
| 45 | + ${Qt5Network_LIBRARIES} | ||
| 46 | + global | ||
| 47 | + logutils | ||
| 48 | + datatypes | ||
| 49 | +) | ||
| 50 | + | ||
| 51 | +include(installation) | ||
| 52 | +install_component() |
src/msgparser.cpp
0 → 100644
| 1 | +++ a/src/msgparser.cpp | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | +#include "msgparser.h" | ||
| 23 | + | ||
| 24 | +namespace osdev { | ||
| 25 | +namespace components { | ||
| 26 | + | ||
| 27 | +const QString MsgParser::s_messageElementTag = "message"; | ||
| 28 | +const QString MsgParser::s_sourceElementTag = "source"; | ||
| 29 | +const QString MsgParser::s_destElementTag = "dest"; | ||
| 30 | +const QString MsgParser::s_actionElementTag = "action"; | ||
| 31 | +const QString MsgParser::s_dataElementTag = "data"; | ||
| 32 | +const QString MsgParser::s_idAttributeTag = "id"; | ||
| 33 | + | ||
| 34 | +MsgParser::MsgParser(QObject* _parent) | ||
| 35 | + : QObject(_parent) | ||
| 36 | + , m_message() | ||
| 37 | + , m_sourceId() | ||
| 38 | + , m_destId() | ||
| 39 | + , m_actionId(-1) | ||
| 40 | + , m_actionData() | ||
| 41 | +{ | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +MsgParser::~MsgParser() | ||
| 45 | +{ | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +void MsgParser::createMsg(const QString& sourceId, const QString& destId, | ||
| 49 | + int actionId, const QString& actionData) | ||
| 50 | +{ | ||
| 51 | + static const QString xmlVar("<%1 %2='%3'/>"); | ||
| 52 | + static const QString xmlData("<%1>%2</%1>"); | ||
| 53 | + static const QString xmlMessage("<%1>%2 %3 %4 %5</%1>"); | ||
| 54 | + | ||
| 55 | + QString src = xmlVar.arg(s_sourceElementTag).arg(s_idAttributeTag).arg(sourceId); | ||
| 56 | + QString dst = xmlVar.arg(s_destElementTag).arg(s_idAttributeTag).arg(destId); | ||
| 57 | + QString action = xmlVar.arg(s_actionElementTag).arg(s_idAttributeTag).arg(actionId); | ||
| 58 | + QString data = xmlData.arg(s_dataElementTag).arg(actionData); | ||
| 59 | + | ||
| 60 | + m_message = xmlMessage.arg(s_messageElementTag).arg(src).arg(dst).arg(action).arg(data); | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +void MsgParser::createMsg(const QString& sourceId, const QString& destId, | ||
| 64 | + int actionId, xercesc_2_7::DOMDocument* actionData) | ||
| 65 | +{ | ||
| 66 | + QString msgData; | ||
| 67 | + | ||
| 68 | + CXmlParser* pSerializer = new CXmlParser(); | ||
| 69 | + // Store the complete message. | ||
| 70 | + msgData = pSerializer->serialize(actionData); | ||
| 71 | + delete pSerializer; | ||
| 72 | + | ||
| 73 | + static const QString xmlVar("<%1 %2='%3'/>"); | ||
| 74 | + static const QString xmlData("<%1>%2</%1>"); | ||
| 75 | + static const QString xmlMessage("<%1>%2 %3 %4 %5</%1>"); | ||
| 76 | + | ||
| 77 | + QString src = xmlVar.arg(s_sourceElementTag).arg(s_idAttributeTag).arg(sourceId); | ||
| 78 | + QString dst = xmlVar.arg(s_destElementTag).arg(s_idAttributeTag).arg(destId); | ||
| 79 | + QString action = xmlVar.arg(s_actionElementTag).arg(s_idAttributeTag).arg(actionId); | ||
| 80 | + QString data = xmlData.arg(s_dataElementTag).arg(msgData); | ||
| 81 | + | ||
| 82 | + m_message = xmlMessage.arg(s_messageElementTag).arg(src).arg(dst).arg(action).arg(data); | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +void MsgParser::parseMsg(const QString& msg) | ||
| 86 | +{ | ||
| 87 | + CXmlParser parser; | ||
| 88 | + | ||
| 89 | + DOMDocument* domDocument = parser.parse(msg); | ||
| 90 | + DOMNode* rootNode = domDocument->getDocumentElement(); | ||
| 91 | + | ||
| 92 | + DOMNode* node = rootNode->getFirstChild(); | ||
| 93 | + if (CXerces::nodeName(node) == sourceElementTag) | ||
| 94 | + { | ||
| 95 | + m_sourceId = CXerces::getNodeAttribute(node, qPrintable(s_idAttributeTag)); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + node = node->getNextSibling(); | ||
| 99 | + if (CXerces::nodeName(node) == destElementTag) | ||
| 100 | + { | ||
| 101 | + m_destId = CXerces::getNodeAttribute(node, qPrintable(s_idAttributeTag)); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + node = node->getNextSibling(); | ||
| 105 | + if (CXerces::nodeName(node) == s_actionElementTag) | ||
| 106 | + { | ||
| 107 | + m_actionId = CXerces::getNodeAttribute(node, qPrintable(s_idAttributeTag)).toInt(); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + node = node->getNextSibling(); | ||
| 111 | + if (CXerces::nodeName(node) == s_dataElementTag) | ||
| 112 | + { | ||
| 113 | + m_actionData = parser.serialize(node); | ||
| 114 | + } | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +QString MsgParser::getMsg() const | ||
| 118 | +{ | ||
| 119 | + return m_message; | ||
| 120 | +} | ||
| 121 | + | ||
| 122 | +QString MsgParser::getSourceId() const | ||
| 123 | +{ | ||
| 124 | + return m_sourceId; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +QString MsgParser::getDestId() const | ||
| 128 | +{ | ||
| 129 | + return m_destId; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +int MsgParser::getActionId() const | ||
| 133 | +{ | ||
| 134 | + return m_actionId; | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +QString MsgParser::getActionData() const | ||
| 138 | +{ | ||
| 139 | + return m_actionData; | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +} | ||
| 143 | +} |
src/msgparser.h
0 → 100644
| 1 | +++ a/src/msgparser.h | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | +// | ||
| 23 | +// Description : | ||
| 24 | +// Revision : | ||
| 25 | +// Confidentiality : | ||
| 26 | +// Dependencies : | ||
| 27 | +// | ||
| 28 | + | ||
| 29 | +#ifndef OSDEV_COMPONENTS_CMSGPARSER_H | ||
| 30 | +#define OSDEV_COMPONENTS_CMSGPARSER_H | ||
| 31 | + | ||
| 32 | +#include <QObject> | ||
| 33 | +#include <QString> | ||
| 34 | +#include <QUuid> | ||
| 35 | + | ||
| 36 | +namespace osdev { | ||
| 37 | +namespace components { | ||
| 38 | + | ||
| 39 | +/** | ||
| 40 | + * @brief The MsgParser class | ||
| 41 | + */ | ||
| 42 | +class MsgParser : public QObject | ||
| 43 | +{ | ||
| 44 | + Q_OBJECT | ||
| 45 | + | ||
| 46 | +public: | ||
| 47 | + /** | ||
| 48 | + * @brief Constructor | ||
| 49 | + * @param _parent Parent QObject | ||
| 50 | + */ | ||
| 51 | + MsgParser(QObject* _parent = nullptr); | ||
| 52 | + /// @brief Destructor | ||
| 53 | + ~MsgParser(); | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * @brief Create a message with the given parameters | ||
| 57 | + * \param sourceID The source ID of the interface calling this function. | ||
| 58 | + * \param destID The destination ID of the interface receiving the data. | ||
| 59 | + * \param actionId The action to be performed on the data. | ||
| 60 | + * \param actionData The actual data (This must be XML formatted) | ||
| 61 | + */ | ||
| 62 | + void createMsg(const QString& sourceID, const QString& destID, | ||
| 63 | + int actionId, const QString& actionData); | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * @brief Retrieve the fields from the specified message | ||
| 67 | + * @param msg Message to parse | ||
| 68 | + */ | ||
| 69 | + void parseMsg(const QString& msg); | ||
| 70 | + | ||
| 71 | + /// @return Message to parse | ||
| 72 | + QString getMsg() const; | ||
| 73 | + /// @return Source ID from the message | ||
| 74 | + QString getSourceId() const; | ||
| 75 | + /// @return Destination ID from the message | ||
| 76 | + QString getDestId() const; | ||
| 77 | + /// @return Action ID from the message | ||
| 78 | + int getActionId() const; | ||
| 79 | + /// @return Action data from the message | ||
| 80 | + QString getActionData() const; | ||
| 81 | + | ||
| 82 | +// void createMsg(const QString& sourceID, const QString& destID, | ||
| 83 | +// int actionId, xercesc_2_7::DOMDocument* actionData); | ||
| 84 | + | ||
| 85 | +private: | ||
| 86 | + QString m_message; ///< Full message | ||
| 87 | + QString m_sourceId; ///< Source ID in the message | ||
| 88 | + QString m_destId; ///< Destination ID in the message | ||
| 89 | + int m_actionId; ///< Action ID in the message | ||
| 90 | + QString m_actionData; ///< Action Data in the message | ||
| 91 | + | ||
| 92 | + static const QString s_messageElementTag; | ||
| 93 | + static const QString s_sourceElementTag; | ||
| 94 | + static const QString s_destElementTag; | ||
| 95 | + static const QString s_actionElementTag; | ||
| 96 | + static const QString s_dataElementTag; | ||
| 97 | + static const QString s_idAttributeTag; | ||
| 98 | +}; | ||
| 99 | + | ||
| 100 | +} // End namespace components | ||
| 101 | +} // End namespace osdev | ||
| 102 | + | ||
| 103 | +#endif /* OSDEV_COMPONENTS_CMSGPARSER_H */ |
src/poolmanager.cpp
0 → 100644
| 1 | +++ a/src/poolmanager.cpp | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | +#include "poolmanager.h" | ||
| 23 | +#include "log.h" | ||
| 24 | +#include <QCoreApplication> | ||
| 25 | + | ||
| 26 | +namespace osdev { | ||
| 27 | +namespace components { | ||
| 28 | + | ||
| 29 | +PoolManager::PoolManager(bool bServer, | ||
| 30 | + const QString& ipAddress, | ||
| 31 | + int portNumber, | ||
| 32 | + int maxTcpConnections, | ||
| 33 | + int maxUdpConnections, | ||
| 34 | + QObject *_parent ) | ||
| 35 | + : QObject( _parent ) | ||
| 36 | + , m_pServer( nullptr ) | ||
| 37 | + , m_pSocketContainer( new SocketContainer() ) | ||
| 38 | + , m_pSocket( nullptr ) | ||
| 39 | + , m_inputBuffer() | ||
| 40 | + , m_outputBuffer() | ||
| 41 | + , m_bServer( bServer ) | ||
| 42 | + , m_ipAddress( ipAddress ) | ||
| 43 | + , m_portNumber( portNumber ) | ||
| 44 | + , m_maxTcpConnections( maxTcpConnections ) | ||
| 45 | + , m_maxUdpConnections( maxUdpConnections ) | ||
| 46 | +{ | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +PoolManager::~PoolManager() | ||
| 50 | +{ | ||
| 51 | + delete m_pSocketContainer; | ||
| 52 | + m_pSocketContainer = nullptr; | ||
| 53 | +} | ||
| 54 | +/*************************************************************************** | ||
| 55 | + * Server Section | ||
| 56 | + ***************************************************************************/ | ||
| 57 | +void PoolManager::slotNewConnection() | ||
| 58 | +{ | ||
| 59 | + // Create the socket, pass the pendingconnection and add it to our channelList. | ||
| 60 | + | ||
| 61 | + QTcpSocket* nextConnection = m_pServer->nextPendingConnection(); | ||
| 62 | + if( nextConnection ) | ||
| 63 | + { | ||
| 64 | + m_pSocket = new TcpSocket( nextConnection ); | ||
| 65 | + m_pSocketContainer->addSocket( m_pSocket ); | ||
| 66 | + | ||
| 67 | + // Connect the socket to the poolmanager | ||
| 68 | + this->connectSocketSignals( m_pSocket ); | ||
| 69 | + } | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +bool PoolManager::startNetworkLayer() | ||
| 73 | +{ | ||
| 74 | + bool bResult = false; | ||
| 75 | + | ||
| 76 | + if( m_bServer ) | ||
| 77 | + { | ||
| 78 | + m_pServer = new QTcpServer; // Create our server object | ||
| 79 | + | ||
| 80 | + connect( m_pServer, SIGNAL( newConnection() ), | ||
| 81 | + this, SLOT( slotNewConnection() ) ); | ||
| 82 | + | ||
| 83 | + m_pServer->setMaxPendingConnections( m_maxTcpConnections ); | ||
| 84 | + bResult = m_pServer->listen( QHostAddress(m_ipAddress), static_cast<quint16>(m_portNumber) ); | ||
| 85 | + if( bResult ) | ||
| 86 | + { | ||
| 87 | + LogDebug("interfaceplugin", "Server started"); | ||
| 88 | + } | ||
| 89 | + else | ||
| 90 | + { | ||
| 91 | + LogError("interfaceplugin", "Server error : " + m_pServer->errorString()); | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + else | ||
| 95 | + { | ||
| 96 | + // Here we start a couple of client connections. | ||
| 97 | + for( int nCounter = 0; nCounter < m_maxTcpConnections; nCounter++ ) | ||
| 98 | + { | ||
| 99 | + m_pSocket = new TcpSocket( m_ipAddress, m_portNumber ); | ||
| 100 | + bResult = m_pSocket->isConnected(); | ||
| 101 | + if( bResult ) | ||
| 102 | + { | ||
| 103 | + m_pSocketContainer->addSocket( m_pSocket ); | ||
| 104 | + this->connectSocketSignals( m_pSocket ); | ||
| 105 | + emit message( 0, 0, osdev::components::gEventConnectionEstablished, 0 ); | ||
| 106 | + } | ||
| 107 | + else | ||
| 108 | + { | ||
| 109 | + LogDebug("[PoolManager::startNetworkLayer()]", QString( "Socket Error : %1" ).arg( m_pSocket->showError() ) ); | ||
| 110 | + delete m_pSocket; | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + } | ||
| 114 | + return bResult; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +void PoolManager::slotDataSent( TcpSocket *pSocket ) | ||
| 118 | +{ | ||
| 119 | + m_pSocketContainer->setChannelFree( pSocket ); | ||
| 120 | + | ||
| 121 | + // Check for delayed messages and send the first one. | ||
| 122 | + this->resendDelayedMessages(); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +void PoolManager::slotReceivingData( TcpSocket *pSocket ) | ||
| 126 | +{ | ||
| 127 | + m_pSocketContainer->setChannelBusy( pSocket ); | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +void PoolManager::slotDataReceived( const QString &sData, TcpSocket *pSocket ) | ||
| 131 | +{ | ||
| 132 | + // Create a unique ticket | ||
| 133 | + QString l_sTicket = createBufferTicket(); | ||
| 134 | + | ||
| 135 | + // Store the dat with the ticket as key | ||
| 136 | + m_outputBuffer.insert( l_sTicket, sData ); | ||
| 137 | + | ||
| 138 | + // Free the channel. | ||
| 139 | + m_pSocketContainer->setChannelFree( pSocket ); | ||
| 140 | + | ||
| 141 | + // Signal the upper layer we have new data available. | ||
| 142 | + emit dataPresent( l_sTicket ); | ||
| 143 | + | ||
| 144 | + // Resend first delayed messages if there are any. | ||
| 145 | + this->resendDelayedMessages(); | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +void PoolManager::sendData( const QString &sData ) | ||
| 149 | +{ | ||
| 150 | + // Get a free channel and signal the data we want to send. | ||
| 151 | + TcpSocket *pSocket = m_pSocketContainer->getFreeSocket(); | ||
| 152 | + if( pSocket ) | ||
| 153 | + { | ||
| 154 | + m_pSocketContainer->setChannelBusy( pSocket ); | ||
| 155 | + emit signalSendData( sData, pSocket ); | ||
| 156 | + QCoreApplication::processEvents(); | ||
| 157 | + } | ||
| 158 | + else | ||
| 159 | + { | ||
| 160 | + LogError("NetworkInterface", QString("No free channel available. Storing for delayed send. Message number: %1").arg(m_inputBuffer.size() + 1)); | ||
| 161 | + m_inputBuffer.append( sData ); | ||
| 162 | + } | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +QString PoolManager::createBufferTicket() | ||
| 166 | +{ | ||
| 167 | + return QUuid::createUuid().toString(); | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +QString PoolManager::getData(const QString& sTicket ) | ||
| 171 | +{ | ||
| 172 | + if( m_outputBuffer.contains( sTicket ) ) | ||
| 173 | + { | ||
| 174 | + return m_outputBuffer.take( sTicket ); | ||
| 175 | + } | ||
| 176 | + else | ||
| 177 | + { | ||
| 178 | + return QString( "[ERROR : ] No data present in outputbuffer under ticket : %1 " ).arg( sTicket ); | ||
| 179 | + } | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +void PoolManager::connectSocketSignals( TcpSocket* l_pSocket) | ||
| 183 | +{ | ||
| 184 | + // First we connect the socket -> poolmanager | ||
| 185 | + connect( l_pSocket, SIGNAL( signalDataReceived( const QString&, TcpSocket* ) ), | ||
| 186 | + this, SLOT( slotDataReceived( const QString&, TcpSocket* ) ) ); | ||
| 187 | + | ||
| 188 | + connect( l_pSocket, SIGNAL( signalDataSent( TcpSocket* ) ), | ||
| 189 | + this, SLOT( slotDataSent( TcpSocket* ) ) ); | ||
| 190 | + | ||
| 191 | + connect( l_pSocket, SIGNAL( signalReceivingData( TcpSocket* ) ), | ||
| 192 | + this, SLOT( slotReceivingData( TcpSocket* ) ) ); | ||
| 193 | + | ||
| 194 | + connect( l_pSocket, SIGNAL( signalConnected( TcpSocket* ) ), | ||
| 195 | + this, SLOT( slotConnected( TcpSocket* ) ) ); | ||
| 196 | + | ||
| 197 | + connect( l_pSocket, SIGNAL( signalDisconnected( TcpSocket* ) ), | ||
| 198 | + this, SLOT( slotDisconnected( TcpSocket* ) ) ); | ||
| 199 | + | ||
| 200 | + // And now the poolmanager -> socket | ||
| 201 | + connect( this, SIGNAL( signalSendData( const QString&, TcpSocket* ) ), | ||
| 202 | + l_pSocket, SLOT( slotSendData( const QString&, TcpSocket* ) ) ); | ||
| 203 | + | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +void PoolManager::resendDelayedMessages() | ||
| 207 | +{ | ||
| 208 | + while( m_inputBuffer.size() > 0 ) // Check for delayed messages | ||
| 209 | + { | ||
| 210 | + if( m_pSocketContainer->getFreeSocket() ) // Check if there is a free channel | ||
| 211 | + { | ||
| 212 | + // Always send the first available message (FIFO) | ||
| 213 | + this->sendData( m_inputBuffer.takeFirst() ); | ||
| 214 | + LogDebug("[PoolManager::resendDelayedMessages]", QString( "Number of queued messages : %1 ").arg( m_inputBuffer.size() ) ); | ||
| 215 | + } | ||
| 216 | + else | ||
| 217 | + { | ||
| 218 | + // Warn if there is a socket available.... | ||
| 219 | + LogDebug( "[PoolManager::resendDelayedMessages]", QString( "No free Socket Available (yet)" ) ); | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +void PoolManager::slotConnected( TcpSocket *pSocket ) | ||
| 225 | +{ | ||
| 226 | + Q_UNUSED( pSocket ); | ||
| 227 | + | ||
| 228 | + emit message( 0, 0, osdev::components::gEventConnectionEstablished, 0 ); | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +void PoolManager::slotDisconnected( TcpSocket *pSocket ) | ||
| 232 | +{ | ||
| 233 | + LogError("NetworkInterface", "Socket disconnected unexpectantly."); | ||
| 234 | + LogError("NetworkInterface", pSocket->showError()); | ||
| 235 | + | ||
| 236 | + // emit message( 0, 0, gEventConnectionLost, 0 ); | ||
| 237 | + | ||
| 238 | + m_pSocketContainer->removeSocket( pSocket ); | ||
| 239 | + delete pSocket; | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +} /* End namespace components */ | ||
| 243 | +} /* End namespace osdev */ |
src/poolmanager.h
0 → 100644
| 1 | +++ a/src/poolmanager.h | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | +#ifndef OSDEV_COMPONENTS_CPOOLMANAGER_H | ||
| 23 | +#define OSDEV_COMPONENTS_CPOOLMANAGER_H | ||
| 24 | + | ||
| 25 | +/*************************************************************************** | ||
| 26 | + * Global Includes | ||
| 27 | + ***************************************************************************/ | ||
| 28 | +#include <QObject> | ||
| 29 | +#include <QString> | ||
| 30 | +#include <QUuid> | ||
| 31 | +#include <QHash> | ||
| 32 | +#include <QTcpServer> | ||
| 33 | +#include <QList> | ||
| 34 | + | ||
| 35 | +/*************************************************************************** | ||
| 36 | + * Local Includes | ||
| 37 | + ***************************************************************************/ | ||
| 38 | +#include "eventcodes.h" // Make the codes known. | ||
| 39 | + | ||
| 40 | +#include "socketcontainer.h" | ||
| 41 | +#include "tcpsocket.h" | ||
| 42 | + | ||
| 43 | +namespace osdev { | ||
| 44 | +namespace components { | ||
| 45 | + | ||
| 46 | +/** | ||
| 47 | + * @brief Handles a pool of network connections | ||
| 48 | + */ | ||
| 49 | +class PoolManager : public QObject | ||
| 50 | +{ | ||
| 51 | + Q_OBJECT | ||
| 52 | + | ||
| 53 | +public: | ||
| 54 | + /** | ||
| 55 | + * @brief Constructor | ||
| 56 | + * @param bServer True for server-mode, false for client-mode (Server is default). | ||
| 57 | + * @param ipAddress IP-address to listen at (server-mode) or to connect to | ||
| 58 | + * (client-mode) | ||
| 59 | + * @param portNumber Portnumber to listen at (server-mode) or to connect to | ||
| 60 | + * (server-mode) | ||
| 61 | + * @param maxTcpConnections Maximum number of TCP connections accepted | ||
| 62 | + * @param maxUdpConnections Maximum number of UDP connections accepted | ||
| 63 | + * (currently unused) | ||
| 64 | + * @param _parent Parent object | ||
| 65 | + */ | ||
| 66 | + PoolManager(bool bServer = false, | ||
| 67 | + const QString& ipAddress = "0.0.0.0", | ||
| 68 | + int portNumber = 3500, | ||
| 69 | + int maxTcpConnections = 10, // The maximum number of connections we accept. | ||
| 70 | + int maxUdpConnections = 10, | ||
| 71 | + QObject *_parent = nullptr ); | ||
| 72 | + | ||
| 73 | + /// @brief Destructor | ||
| 74 | + ~PoolManager(); | ||
| 75 | + | ||
| 76 | + /// Deleted copy-constructor | ||
| 77 | + PoolManager(const PoolManager&) = delete; | ||
| 78 | + /// Deleted assignment operator | ||
| 79 | + PoolManager& operator=(const PoolManager&) = delete; | ||
| 80 | + /// Deleted move-constructor | ||
| 81 | + PoolManager(PoolManager&&) = delete; | ||
| 82 | + /// Deleted move operator | ||
| 83 | + PoolManager& operator=(PoolManager&&) = delete; | ||
| 84 | + | ||
| 85 | + /** | ||
| 86 | + * @brief Get the data for a specified ticket | ||
| 87 | + * @param sTicket Ticket ID | ||
| 88 | + * @return Available data | ||
| 89 | + */ | ||
| 90 | + QString getData( const QString& sTicket ); | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * @brief Start the actual server (server-mode) or build the client | ||
| 94 | + * connection (client-mode) | ||
| 95 | + * @return True on success, false on failure | ||
| 96 | + */ | ||
| 97 | + bool startNetworkLayer(); | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * @brief Send data through the interface | ||
| 101 | + * @param sData Data to send | ||
| 102 | + */ | ||
| 103 | + void sendData( const QString &sData ); | ||
| 104 | + | ||
| 105 | +public slots: | ||
| 106 | + /// @brief Slot called when a new server-connection is available | ||
| 107 | + void slotNewConnection(); | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * @brief Slot called when data was sent through the specified socket | ||
| 111 | + * @param pSocket Socket through which data was sent | ||
| 112 | + */ | ||
| 113 | + void slotDataSent( TcpSocket *pSocket ); | ||
| 114 | + | ||
| 115 | + /** | ||
| 116 | + * @brief Slot called when data is being received by the specified socket | ||
| 117 | + * @param pSocket Socket which is receiving data | ||
| 118 | + */ | ||
| 119 | + void slotReceivingData( TcpSocket *pSocket ); | ||
| 120 | + | ||
| 121 | + /** | ||
| 122 | + * @brief Slot called when data was received through the specified socket | ||
| 123 | + * @param sData Data received | ||
| 124 | + * @param pSocket Socket through which data was sent | ||
| 125 | + */ | ||
| 126 | + void slotDataReceived( const QString &sData, TcpSocket *pSocket ); | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * @brief Slot called when the specified socket was connected | ||
| 130 | + * @param pSocket Socket through which data was sent | ||
| 131 | + */ | ||
| 132 | + void slotConnected( TcpSocket *pSocket ); | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * @brief Slot called when the specified socket was disconnected | ||
| 136 | + * @param pSocket Socket that was disconnected | ||
| 137 | + * | ||
| 138 | + * Also removes the socket from the list of known sockets and deletes it | ||
| 139 | + */ | ||
| 140 | + void slotDisconnected( TcpSocket *pSocket ); | ||
| 141 | + | ||
| 142 | +signals: | ||
| 143 | + /** | ||
| 144 | + * @brief Signal emitted when data is sent through a specified socket | ||
| 145 | + * @param sData Data to be sent | ||
| 146 | + * @param pSocket Socket that sends the data | ||
| 147 | + */ | ||
| 148 | + void signalSendData( const QString &sData, TcpSocket *pSocket ); | ||
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * @brief Signal emitted when data is present for the specified ticket | ||
| 152 | + * @param sTicket Ticket ID | ||
| 153 | + */ | ||
| 154 | + void dataPresent( const QString &sTicket ); | ||
| 155 | + | ||
| 156 | + /** | ||
| 157 | + * @brief Relay the message signal to the eventmanager | ||
| 158 | + * @todo Check parameter meanings! | ||
| 159 | + * @param i1 sourceID ? | ||
| 160 | + * @param i2 DestID ? | ||
| 161 | + * @param i3 Connection status ? | ||
| 162 | + * @param i4 data ? | ||
| 163 | + */ | ||
| 164 | + void message( int i1, int i2, int i3, int i4 ); | ||
| 165 | + | ||
| 166 | +private: | ||
| 167 | + /** | ||
| 168 | + * @brief Create a new unique buffer ticket ID | ||
| 169 | + * @return New ticket ID | ||
| 170 | + */ | ||
| 171 | + QString createBufferTicket(); | ||
| 172 | + | ||
| 173 | + /** | ||
| 174 | + * @brief Connect all signals and slots for the socket | ||
| 175 | + * @param l_pSocket Socket to connect | ||
| 176 | + */ | ||
| 177 | + void connectSocketSignals( TcpSocket *l_pSocket ); | ||
| 178 | + | ||
| 179 | + /// @brief Resend messages that are still in the buffer | ||
| 180 | + void resendDelayedMessages(); | ||
| 181 | + | ||
| 182 | + QTcpServer *m_pServer; ///< The pointer to the server object. | ||
| 183 | + SocketContainer *m_pSocketContainer; ///< Administration which keeps track of the Sockets. | ||
| 184 | + TcpSocket *m_pSocket; | ||
| 185 | + | ||
| 186 | + QList< QString > m_inputBuffer; ///< Messages that can not be send yet will be stored here. | ||
| 187 | + QHash< QString, QString > m_outputBuffer; ///< The hashtable we use for databuffering. | ||
| 188 | + | ||
| 189 | + bool m_bServer; ///< True for server-mode, false for client-mode | ||
| 190 | + QString m_ipAddress; ///< The ipAddress the clients are connecting to. | ||
| 191 | + int m_portNumber; ///< The portnumber our server is listening on. | ||
| 192 | + int m_maxTcpConnections;///< The number of TCP connections we accept and make. | ||
| 193 | + int m_maxUdpConnections;///< The number of UDP connections we accept and make. | ||
| 194 | +}; | ||
| 195 | + | ||
| 196 | +} // End namespace components | ||
| 197 | +} // End namespace osdev | ||
| 198 | + | ||
| 199 | +#endif /* OSDEV_COMPONENTS_CPOOLMANAGER_H */ |
src/socketcontainer.cpp
0 → 100644
| 1 | +++ a/src/socketcontainer.cpp | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | + | ||
| 23 | +#include "socketcontainer.h" | ||
| 24 | + | ||
| 25 | +#include "tcpsocket.h" | ||
| 26 | +#include "log.h" | ||
| 27 | + | ||
| 28 | +namespace osdev { | ||
| 29 | +namespace components { | ||
| 30 | + | ||
| 31 | +SocketContainer::SocketContainer() | ||
| 32 | + : m_hshFreeBySocket() | ||
| 33 | +{ | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +SocketContainer::~SocketContainer() | ||
| 37 | +{ | ||
| 38 | + // qDeleteAll( hshFreeBySocket.begin(), hshFreeBySocket.end() ); | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +void SocketContainer::addSocket( TcpSocket *pSocket ) | ||
| 42 | +{ | ||
| 43 | + if( pSocket ) | ||
| 44 | + { | ||
| 45 | + // We store a channel in a ready state. | ||
| 46 | + m_hshFreeBySocket.insert( pSocket, true ); | ||
| 47 | + } | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +void SocketContainer::removeSocket( TcpSocket *pSocket ) | ||
| 51 | +{ | ||
| 52 | + if( pSocket ) | ||
| 53 | + { | ||
| 54 | + // Remove the channel from the list. | ||
| 55 | + m_hshFreeBySocket.remove( pSocket ); | ||
| 56 | + } | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +void SocketContainer::setChannelFree( TcpSocket *pSocket ) | ||
| 60 | +{ | ||
| 61 | + if( pSocket ) | ||
| 62 | + { | ||
| 63 | + auto socketIt = m_hshFreeBySocket.find(pSocket); | ||
| 64 | + if( m_hshFreeBySocket.end() != socketIt ) | ||
| 65 | + { | ||
| 66 | + socketIt.value() = true; | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +void SocketContainer::setChannelBusy(TcpSocket *pSocket) | ||
| 72 | +{ | ||
| 73 | + if( pSocket ) | ||
| 74 | + { | ||
| 75 | + auto socketIt = m_hshFreeBySocket.find(pSocket); | ||
| 76 | + if( m_hshFreeBySocket.end() != socketIt ) | ||
| 77 | + { | ||
| 78 | + socketIt.value() = false; | ||
| 79 | + | ||
| 80 | + // printStatus(); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +TcpSocket* SocketContainer::getFreeSocket() const | ||
| 86 | +{ | ||
| 87 | + // printStatus(); | ||
| 88 | + | ||
| 89 | + // Return a random socket with a free channel | ||
| 90 | + return m_hshFreeBySocket.key( true ); | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +void SocketContainer::printStatus() const | ||
| 94 | +{ | ||
| 95 | + LogDebug("interfaceplugin", QString("getFreeSocket counts : ") + m_hshFreeBySocket.count()); | ||
| 96 | + LogDebug("interfaceplugin", QString("===========================================================")); | ||
| 97 | + | ||
| 98 | + for(auto it = m_hshFreeBySocket.begin(); it != m_hshFreeBySocket.end(); ++it) | ||
| 99 | + { | ||
| 100 | + LogDebug("interfaceplugin", it.key()->objectName()+" : "+it.value()); | ||
| 101 | + } | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +} // End namespace components | ||
| 105 | +} // End namespace osdev |
src/socketcontainer.h
0 → 100644
| 1 | +++ a/src/socketcontainer.h | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | + | ||
| 23 | +#ifndef OSDEV_COMPONENTS_CTHREADCONTAINER_H | ||
| 24 | +#define OSDEV_COMPONENTS_CTHREADCONTAINER_H | ||
| 25 | + | ||
| 26 | +/*************************************************************************** | ||
| 27 | + * Global Includes | ||
| 28 | + ***************************************************************************/ | ||
| 29 | +#include <QHash> | ||
| 30 | + | ||
| 31 | +/*************************************************************************** | ||
| 32 | + * Local Includes | ||
| 33 | + ***************************************************************************/ | ||
| 34 | + | ||
| 35 | +namespace osdev { | ||
| 36 | +namespace components { | ||
| 37 | + | ||
| 38 | +class TcpSocket; | ||
| 39 | + | ||
| 40 | +/** | ||
| 41 | + * @brief Maintains a collection of sockets | ||
| 42 | + * | ||
| 43 | + * @note This class does NOT take ownership of the provided sockets | ||
| 44 | + */ | ||
| 45 | +class SocketContainer | ||
| 46 | +{ | ||
| 47 | +public: | ||
| 48 | + /// Constructor | ||
| 49 | + SocketContainer(); | ||
| 50 | + /// Destructor | ||
| 51 | + ~SocketContainer(); | ||
| 52 | + | ||
| 53 | + /// Deleted copy-constructor | ||
| 54 | + SocketContainer(const SocketContainer&) = delete; | ||
| 55 | + /// Deleted assignment operator | ||
| 56 | + SocketContainer& operator=(const SocketContainer&) = delete; | ||
| 57 | + /// Deleted move-constructor | ||
| 58 | + SocketContainer(SocketContainer&&) = delete; | ||
| 59 | + /// Deleted move operator | ||
| 60 | + SocketContainer& operator=(SocketContainer&&) = delete; | ||
| 61 | + | ||
| 62 | + //! This function will set the given thread to free | ||
| 63 | + //! \param pSocket Socket to set to free | ||
| 64 | + void setChannelFree( TcpSocket *pSocket ); | ||
| 65 | + | ||
| 66 | + //! This function will set the given thread to busy | ||
| 67 | + //! \param pSocket Socket to set to busy | ||
| 68 | + void setChannelBusy( TcpSocket *pSocket ); | ||
| 69 | + | ||
| 70 | + //! This function will add a newly created thread to the container | ||
| 71 | + //! \param pSocket Add a new socket to the list | ||
| 72 | + void addSocket( TcpSocket *pSocket ); | ||
| 73 | + | ||
| 74 | + //! This function removes a thread from the list for whatever reason there might be | ||
| 75 | + //! \param pSocket Removes the socket from the list | ||
| 76 | + void removeSocket( TcpSocket *pSocket ); | ||
| 77 | + | ||
| 78 | + //! @return This function will return the first free thread. | ||
| 79 | + TcpSocket* getFreeSocket() const; | ||
| 80 | + | ||
| 81 | +private: | ||
| 82 | + /// Dump the status to debug log | ||
| 83 | + void printStatus() const; | ||
| 84 | + | ||
| 85 | + /// Contains all managed sockets | ||
| 86 | + QHash< TcpSocket*, bool > m_hshFreeBySocket; | ||
| 87 | +}; | ||
| 88 | + | ||
| 89 | +} // End namespace components | ||
| 90 | +} // End namespace osdev | ||
| 91 | + | ||
| 92 | +#endif /* OSDEV_COMPONENTS_CTHREADCONTAINER_H */ |
src/tcpinterface.cpp
0 → 100644
| 1 | +++ a/src/tcpinterface.cpp | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | + | ||
| 23 | +#include "tcpinterface.h" | ||
| 24 | +#include "log.h" | ||
| 25 | + | ||
| 26 | +#include <QHostAddress> | ||
| 27 | +#include <QTcpServer> | ||
| 28 | +#include <QTcpSocket> | ||
| 29 | +#include <QDataStream> | ||
| 30 | + | ||
| 31 | +namespace osdev { | ||
| 32 | +namespace components { | ||
| 33 | + | ||
| 34 | +TcpInterface::TcpInterface(const QString& hostName, int port, bool i_bServer) | ||
| 35 | + : m_bServer( i_bServer ), | ||
| 36 | + m_active(false), | ||
| 37 | + m_tcpServer( nullptr ), | ||
| 38 | + m_tcpSocket( nullptr ), | ||
| 39 | + m_clientConnection( nullptr ), | ||
| 40 | + m_blockSize( 0 ), | ||
| 41 | + m_dataList() | ||
| 42 | +{ | ||
| 43 | + QHostAddress hostAddress(hostName); | ||
| 44 | + | ||
| 45 | + if (m_bServer) | ||
| 46 | + { // This interface is a server | ||
| 47 | + m_tcpServer = new QTcpServer(this); | ||
| 48 | + connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); | ||
| 49 | + m_active = m_tcpServer->listen(hostAddress, static_cast<quint16>(port)); | ||
| 50 | + if (!m_active) | ||
| 51 | + { | ||
| 52 | + LogWarning("interfaceplugin", | ||
| 53 | + "Unable to start the server: " + m_tcpServer->errorString()); | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + else | ||
| 57 | + { // This interface is a client | ||
| 58 | + m_tcpSocket = new QTcpSocket(this); | ||
| 59 | + m_tcpSocket->connectToHost(hostAddress, static_cast<quint16>(port)); | ||
| 60 | + m_active = m_tcpSocket->waitForConnected(1000); | ||
| 61 | + if (m_active) | ||
| 62 | + { | ||
| 63 | + connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readData())); | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | + | ||
| 69 | +TcpInterface::~TcpInterface() | ||
| 70 | +{ | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +QString TcpInterface::getData() | ||
| 74 | +{ | ||
| 75 | + QString qsResult; | ||
| 76 | + if(!m_dataList.isEmpty()) | ||
| 77 | + { | ||
| 78 | + qsResult = m_dataList.takeFirst(); | ||
| 79 | + } | ||
| 80 | + return qsResult; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +void TcpInterface::readData() | ||
| 84 | +{ | ||
| 85 | + QTcpSocket* pSocket = nullptr; | ||
| 86 | + /* Retrieve the correct Socket, depending on connection-type */ | ||
| 87 | + if (m_bServer) | ||
| 88 | + { | ||
| 89 | + pSocket = m_clientConnection; | ||
| 90 | + } | ||
| 91 | + else | ||
| 92 | + { | ||
| 93 | + pSocket = m_tcpSocket; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + /* Construct a DataStream from the Socket */ | ||
| 97 | + QDataStream in(pSocket); | ||
| 98 | + in.setVersion(QDataStream::Qt_5_4); | ||
| 99 | + | ||
| 100 | + // Process while there is a following message in this same buffer. | ||
| 101 | + do { | ||
| 102 | + LogDebug("TcpInterface", "read bytes"); | ||
| 103 | + /* The size of the message in bytes is required */ | ||
| 104 | + if (m_blockSize == 0) | ||
| 105 | + { | ||
| 106 | + /* | ||
| 107 | + * The size of the message should be available as the first four | ||
| 108 | + * blocks of the message; i.e. (sizeof(quint32)) | ||
| 109 | + */ | ||
| 110 | + if (pSocket->bytesAvailable() < static_cast<int>(sizeof(quint32))) | ||
| 111 | + { | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + /* Retrieve the blocksize if it is fully available from the socket */ | ||
| 115 | + in >> m_blockSize; | ||
| 116 | + LogDebug("TcpInterface", QString("block size is %1").arg(m_blockSize)); | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + if (pSocket->bytesAvailable() < m_blockSize) | ||
| 120 | + { | ||
| 121 | + return; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + /* | ||
| 125 | + * Retrieve the message if it is completely available from the socket. | ||
| 126 | + * I.e.: The number of available bytes must be at least the retrieved | ||
| 127 | + * message-size. | ||
| 128 | + */ | ||
| 129 | + QByteArray pDataStore; | ||
| 130 | + /* | ||
| 131 | + * Using an empty QByteArray the operator>> can be used, because it then | ||
| 132 | + * reads in everything until the first '\0' character. | ||
| 133 | + * It is then required that all messages are ended by a '\0'. This is | ||
| 134 | + * standard when sending QByteArrays. | ||
| 135 | + */ | ||
| 136 | + in >> pDataStore; | ||
| 137 | + /* | ||
| 138 | + * An entire message is stored in a new QByteArray and must be pushed on | ||
| 139 | + * the stack of available messages | ||
| 140 | + */ | ||
| 141 | + m_dataList.append( pDataStore ); | ||
| 142 | + m_blockSize = 0; // reset blocksize for next read | ||
| 143 | + emit dataPresent(); | ||
| 144 | + } while (pSocket->bytesAvailable() > 0); | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +void TcpInterface::sendData(const QString& i_qsData) | ||
| 148 | +{ | ||
| 149 | + QByteArray block; | ||
| 150 | + QDataStream out(&block, QIODevice::WriteOnly); | ||
| 151 | + | ||
| 152 | + out.setVersion(QDataStream::Qt_5_4); | ||
| 153 | + | ||
| 154 | + out << block.size(); | ||
| 155 | + out << i_qsData.toLatin1(); | ||
| 156 | + | ||
| 157 | + if (m_bServer) | ||
| 158 | + { | ||
| 159 | + m_clientConnection->write(block); | ||
| 160 | + m_clientConnection->flush(); | ||
| 161 | + } | ||
| 162 | + else | ||
| 163 | + { | ||
| 164 | + m_tcpSocket->write(block); | ||
| 165 | + m_tcpSocket->flush(); | ||
| 166 | + } | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +void TcpInterface::newConnection() | ||
| 170 | +{ | ||
| 171 | + m_clientConnection = m_tcpServer->nextPendingConnection(); | ||
| 172 | + connect(m_clientConnection, SIGNAL(readyRead()), this, SLOT(readData())); | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +} | ||
| 176 | +} |
src/tcpinterface.h
0 → 100644
| 1 | +++ a/src/tcpinterface.h | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | + | ||
| 23 | +#ifndef OSDEV_COMPONENTS_CTCPINTERFACE_H | ||
| 24 | +#define OSDEV_COMPONENTS_CTCPINTERFACE_H | ||
| 25 | + | ||
| 26 | +#include <QObject> | ||
| 27 | +#include <QList> | ||
| 28 | + | ||
| 29 | +class QTcpServer; | ||
| 30 | +class QTcpSocket; | ||
| 31 | + | ||
| 32 | +namespace osdev { | ||
| 33 | +namespace components { | ||
| 34 | + | ||
| 35 | +/** | ||
| 36 | + * @brief Handles a generic client or server point-to-point connection | ||
| 37 | + * | ||
| 38 | + * This class can be instantiated as a client or a server. | ||
| 39 | + * | ||
| 40 | + * @note The server can currently only handle one client at a time. | ||
| 41 | + */ | ||
| 42 | +class TcpInterface : public QObject | ||
| 43 | +{ | ||
| 44 | + Q_OBJECT | ||
| 45 | + | ||
| 46 | +public: | ||
| 47 | + /** | ||
| 48 | + * @brief Constructor | ||
| 49 | + * @param hostName Host to connect with (client-mode), or interface to | ||
| 50 | + * connect at (server-mode) | ||
| 51 | + * @param port Selected port-number | ||
| 52 | + * @param i_bServer False for client-mode, true for server-mode | ||
| 53 | + */ | ||
| 54 | + TcpInterface(const QString& hostName, int port = 2776, bool i_bServer = false); | ||
| 55 | + /// @brief Destructor | ||
| 56 | + ~TcpInterface(); | ||
| 57 | + | ||
| 58 | + /// Deleted copy-constructor | ||
| 59 | + TcpInterface(const TcpInterface&) = delete; | ||
| 60 | + /// Deleted assignment operator | ||
| 61 | + TcpInterface& operator=(const TcpInterface&) = delete; | ||
| 62 | + /// Deleted move-constructor | ||
| 63 | + TcpInterface(TcpInterface&&) = delete; | ||
| 64 | + /// Deleted move operator | ||
| 65 | + TcpInterface& operator=(TcpInterface&&) = delete; | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * @return if the interface is active. | ||
| 69 | + * In server mode the interface is listening or connected to and in | ||
| 70 | + * client mode the interface is connected. | ||
| 71 | + */ | ||
| 72 | + bool active() const | ||
| 73 | + { | ||
| 74 | + return m_active; | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * @brief Reads the first block of data from the received data | ||
| 79 | + * @return First block of data, as a QString | ||
| 80 | + */ | ||
| 81 | + QString getData(); | ||
| 82 | + | ||
| 83 | +public slots: | ||
| 84 | + | ||
| 85 | + /** | ||
| 86 | + * @brief Called when data is ready to be read into the buffer | ||
| 87 | + */ | ||
| 88 | + void readData(); | ||
| 89 | + | ||
| 90 | + /** | ||
| 91 | + * @brief Send a block of data to the other party | ||
| 92 | + * @param i_qsData Data to be sent | ||
| 93 | + */ | ||
| 94 | + void sendData(const QString& i_qsData); | ||
| 95 | + | ||
| 96 | +signals: | ||
| 97 | + /// @brief Emitted when data is present on the connection | ||
| 98 | + void dataPresent(); | ||
| 99 | + | ||
| 100 | +private slots: | ||
| 101 | + /// @brief Slot called when a new connection is made | ||
| 102 | + void newConnection(); | ||
| 103 | + | ||
| 104 | +private: | ||
| 105 | + bool m_bServer; ///< True for server-mode, false for client-mode | ||
| 106 | + bool m_active; ///< True if in server mode the server is listening or connected to and in client mode when the client is connected. | ||
| 107 | + | ||
| 108 | + QTcpServer* m_tcpServer; ///< Server object instance | ||
| 109 | + QTcpSocket* m_tcpSocket; ///< Client object instance | ||
| 110 | + | ||
| 111 | + QTcpSocket* m_clientConnection; ///< Current client connection | ||
| 112 | + | ||
| 113 | + quint32 m_blockSize; ///< Maximum size of a data-block | ||
| 114 | + QList<QByteArray> m_dataList; ///< Blocks of data read from the counterpart | ||
| 115 | +}; | ||
| 116 | + | ||
| 117 | +} // End namespace components | ||
| 118 | +} // End namespace osdev | ||
| 119 | + | ||
| 120 | +#endif /* OSDEV_COMPONENTS_CTCPINTERFACE_H */ |
src/tcpsocket.cpp
0 → 100644
| 1 | +++ a/src/tcpsocket.cpp | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | +#include "tcpsocket.h" | ||
| 23 | + | ||
| 24 | +#include "log.h" | ||
| 25 | + | ||
| 26 | +#include <QHostAddress> | ||
| 27 | +#include <QTcpSocket> | ||
| 28 | +#include <QTimer> | ||
| 29 | +#include <QDataStream> | ||
| 30 | + | ||
| 31 | +namespace osdev { | ||
| 32 | +namespace components { | ||
| 33 | + | ||
| 34 | +TcpSocket::TcpSocket( const QString& ipAddress, int portNumber ) | ||
| 35 | + : m_pSocket(new QTcpSocket(this)) | ||
| 36 | + , m_blockSize(0) | ||
| 37 | + , m_dataBuffer() | ||
| 38 | + , m_pTimer( new QTimer() ) | ||
| 39 | +{ | ||
| 40 | + // First we connect the timer to this class | ||
| 41 | + connect(m_pTimer.get(), SIGNAL(timeout()), this, SLOT(chkSendBuffer())); | ||
| 42 | + | ||
| 43 | + this->connectSocketSignals(); | ||
| 44 | + | ||
| 45 | + m_pSocket->connectToHost( QHostAddress( ipAddress ), static_cast<quint16>(portNumber) ); | ||
| 46 | + if( !m_pSocket->waitForConnected( 3000 ) ) | ||
| 47 | + { | ||
| 48 | + LogDebug("interfaceplugin", "Socket failed to connect."); | ||
| 49 | + LogObject("interfaceplugin", this); | ||
| 50 | + } | ||
| 51 | + else | ||
| 52 | + { | ||
| 53 | + LogDebug("interfaceplugin", "Client connected : "); | ||
| 54 | + LogObject("interfaceplugin", m_pSocket); | ||
| 55 | + } | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +TcpSocket::TcpSocket(QTcpSocket *pSocket) | ||
| 59 | + : m_pSocket(pSocket) | ||
| 60 | + , m_blockSize(0) | ||
| 61 | + , m_dataBuffer() | ||
| 62 | + , m_pTimer( new QTimer() ) | ||
| 63 | +{ | ||
| 64 | + Q_ASSERT(pSocket); | ||
| 65 | + | ||
| 66 | + connect(m_pTimer.get(), SIGNAL(timeout()), this, SLOT(chkSendBuffer())); | ||
| 67 | + | ||
| 68 | + this->connectSocketSignals(); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +TcpSocket::~TcpSocket() | ||
| 72 | +{ | ||
| 73 | + // Implicitly deletes m_pSocket if this object is its parent | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +void TcpSocket::slotReadyRead() | ||
| 77 | +{ | ||
| 78 | + // Let the poolmanager know we are busy receiving data at the moment and are | ||
| 79 | + // unavailable for anything else. | ||
| 80 | + emit signalReceivingData( this ); | ||
| 81 | + | ||
| 82 | + QDataStream in( m_pSocket ); | ||
| 83 | + in.setVersion( QDataStream::Qt_5_4 ); | ||
| 84 | + | ||
| 85 | + if( m_blockSize == 0 ) | ||
| 86 | + { | ||
| 87 | + if( m_pSocket->bytesAvailable() < static_cast<int>(sizeof( quint32 ) ) ) | ||
| 88 | + { | ||
| 89 | + return; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + in >> m_blockSize; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + if( m_pSocket->bytesAvailable() < m_blockSize ) | ||
| 96 | + { | ||
| 97 | + return; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + in >> m_dataBuffer; | ||
| 101 | + | ||
| 102 | + emit signalDataReceived( m_dataBuffer, this ); | ||
| 103 | + | ||
| 104 | + m_blockSize = 0; // Reset the blockSize for next read. | ||
| 105 | + m_dataBuffer.clear(); // Reset the buffer for the next read. | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +void TcpSocket::slotSendData( const QString &sData, TcpSocket *pSocket ) | ||
| 109 | +{ | ||
| 110 | + if( pSocket != this || sData.isNull() ) { | ||
| 111 | + return; | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + QByteArray block; | ||
| 115 | + QDataStream out( &block, QIODevice::WriteOnly ); | ||
| 116 | + | ||
| 117 | + out.setVersion( QDataStream::Qt_5_4 ); | ||
| 118 | + out << block.size(); | ||
| 119 | + out << sData; | ||
| 120 | + | ||
| 121 | + m_pSocket->write( block ); | ||
| 122 | + chkSendBuffer(); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +void TcpSocket::slotSetSocket(QTcpSocket* pSocket ) | ||
| 126 | +{ | ||
| 127 | + if(m_pSocket) { | ||
| 128 | + disconnectSocketSignals(); | ||
| 129 | + } | ||
| 130 | + m_pSocket = pSocket; | ||
| 131 | + connectSocketSignals(); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +void TcpSocket::chkSendBuffer() | ||
| 135 | +{ | ||
| 136 | + m_pTimer->stop(); | ||
| 137 | + | ||
| 138 | + if( m_pSocket->bytesToWrite() > 0 ) | ||
| 139 | + { | ||
| 140 | + m_pSocket->flush(); | ||
| 141 | + m_pTimer->start( 500 ); | ||
| 142 | + } | ||
| 143 | + else | ||
| 144 | + { | ||
| 145 | + emit signalDataSent( this ); | ||
| 146 | + } | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +void TcpSocket::slotConnected() | ||
| 150 | +{ | ||
| 151 | + emit signalConnected( this ); | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +void TcpSocket::slotDisconnected() | ||
| 155 | +{ | ||
| 156 | + emit signalDisconnected( this ); | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +QString TcpSocket::showError() | ||
| 160 | +{ | ||
| 161 | + if( m_pSocket ) | ||
| 162 | + { | ||
| 163 | + return m_pSocket->errorString(); | ||
| 164 | + } | ||
| 165 | + else | ||
| 166 | + { | ||
| 167 | + return "No socket available"; | ||
| 168 | + } | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +void TcpSocket::connectSocketSignals() | ||
| 172 | +{ | ||
| 173 | + Q_ASSERT(m_pSocket); | ||
| 174 | + // Connect the required signals and slots to gain control. | ||
| 175 | + // First for receiving data. | ||
| 176 | + connect( m_pSocket, SIGNAL( readyRead() ), | ||
| 177 | + this, SLOT( slotReadyRead() ) ); | ||
| 178 | + | ||
| 179 | + connect( m_pSocket, SIGNAL( connected() ), | ||
| 180 | + this, SLOT( slotConnected() ) ); | ||
| 181 | + | ||
| 182 | + connect( m_pSocket, SIGNAL( disconnected() ), | ||
| 183 | + this, SLOT( slotDisconnected() ) ); | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +void TcpSocket::disconnectSocketSignals() | ||
| 187 | +{ | ||
| 188 | + Q_ASSERT(m_pSocket); | ||
| 189 | + // Connect the required signals and slots to gain control. | ||
| 190 | + // First for receiving data. | ||
| 191 | + disconnect( m_pSocket, SIGNAL( readyRead() ), | ||
| 192 | + this, SLOT( slotReadyRead() ) ); | ||
| 193 | + | ||
| 194 | + disconnect( m_pSocket, SIGNAL( connected() ), | ||
| 195 | + this, SLOT( slotConnected() ) ); | ||
| 196 | + | ||
| 197 | + disconnect( m_pSocket, SIGNAL( disconnected() ), | ||
| 198 | + this, SLOT( slotDisconnected() ) ); | ||
| 199 | +} | ||
| 200 | + | ||
| 201 | +bool TcpSocket::isConnected() | ||
| 202 | +{ | ||
| 203 | + return m_pSocket && m_pSocket->state() == QTcpSocket::ConnectedState; | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +} | ||
| 207 | +} |
src/tcpsocket.h
0 → 100644
| 1 | +++ a/src/tcpsocket.h | ||
| 1 | +/* **************************************************************************** | ||
| 2 | + * Copyright 2019 Open Systems Development BV * | ||
| 3 | + * * | ||
| 4 | + * Permission is hereby granted, free of charge, to any person obtaining a * | ||
| 5 | + * copy of this software and associated documentation files (the "Software"), * | ||
| 6 | + * to deal in the Software without restriction, including without limitation * | ||
| 7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, * | ||
| 8 | + * and/or sell copies of the Software, and to permit persons to whom the * | ||
| 9 | + * Software is furnished to do so, subject to the following conditions: * | ||
| 10 | + * * | ||
| 11 | + * The above copyright notice and this permission notice shall be included in * | ||
| 12 | + * all copies or substantial portions of the Software. * | ||
| 13 | + * * | ||
| 14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * | ||
| 15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * | ||
| 16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * | ||
| 17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * | ||
| 18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * | ||
| 19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * | ||
| 20 | + * DEALINGS IN THE SOFTWARE. * | ||
| 21 | + * ***************************************************************************/ | ||
| 22 | + | ||
| 23 | +#ifndef OSDEV_COMPONENTS_CTCPSOCKET_H | ||
| 24 | +#define OSDEV_COMPONENTS_CTCPSOCKET_H | ||
| 25 | + | ||
| 26 | +#include <QObject> | ||
| 27 | +//#include <QTimer> | ||
| 28 | +//#include <QTcpSocket> | ||
| 29 | +//#include <QHostAddress> | ||
| 30 | +//#include <QDataStream> | ||
| 31 | +//#include <QString> | ||
| 32 | + | ||
| 33 | +#include <memory> | ||
| 34 | + | ||
| 35 | +class QTcpSocket; | ||
| 36 | +class QTimer; | ||
| 37 | + | ||
| 38 | +namespace osdev { | ||
| 39 | +namespace components { | ||
| 40 | + | ||
| 41 | +/** | ||
| 42 | + * @brief Wrapper around QTcpSocket | ||
| 43 | + */ | ||
| 44 | +class TcpSocket : public QObject | ||
| 45 | +{ | ||
| 46 | + Q_OBJECT | ||
| 47 | + | ||
| 48 | +public: | ||
| 49 | + /** | ||
| 50 | + * @brief Create a TcpSocket connecting to the given address | ||
| 51 | + * @param ipAddress Host to connect to | ||
| 52 | + * @param portNumber Port number to connect at | ||
| 53 | + */ | ||
| 54 | + TcpSocket( const QString& ipAddress, int portNumber ); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * @brief Create a TcpSocket based on an existing QTcpSocket | ||
| 58 | + * @param pSocket Socket used. This class does NOT take ownership of the | ||
| 59 | + * provided socket. | ||
| 60 | + */ | ||
| 61 | + TcpSocket( QTcpSocket *pSocket ); | ||
| 62 | + | ||
| 63 | + /// Deleted copy-constructor | ||
| 64 | + TcpSocket(const TcpSocket&) = delete; | ||
| 65 | + /// Deleted assignment operator | ||
| 66 | + TcpSocket& operator=(const TcpSocket&) = delete; | ||
| 67 | + /// Deleted move-constructor | ||
| 68 | + TcpSocket(TcpSocket&&) = delete; | ||
| 69 | + /// Deleted move operator | ||
| 70 | + TcpSocket& operator=(TcpSocket&&) = delete; | ||
| 71 | + | ||
| 72 | + /// @brief Destructor | ||
| 73 | + ~TcpSocket(); | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * @brief Retrieve the last error registered in the socket | ||
| 77 | + * @return Error string | ||
| 78 | + */ | ||
| 79 | + QString showError(); | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * @brief See if a connection is available | ||
| 83 | + * @return True if a connection is available, false otherwise | ||
| 84 | + */ | ||
| 85 | + bool isConnected(); | ||
| 86 | + | ||
| 87 | +public slots: | ||
| 88 | + /** | ||
| 89 | + * @brief Slot called to send data | ||
| 90 | + * @param sData Data to send | ||
| 91 | + * @param pSocket Socket to which to send. If it is not equal to this, the | ||
| 92 | + * slot does nothing and returns | ||
| 93 | + */ | ||
| 94 | + void slotSendData( const QString &sData, TcpSocket *pSocket ); | ||
| 95 | + | ||
| 96 | + /** | ||
| 97 | + * @brief Updates the current QTcpSocket used | ||
| 98 | + * @param pSocket New socket | ||
| 99 | + */ | ||
| 100 | + void slotSetSocket( QTcpSocket *pSocket ); | ||
| 101 | + | ||
| 102 | + /// @brief Slot called when a socket gets connected | ||
| 103 | + void slotConnected(); | ||
| 104 | + | ||
| 105 | + /// @brief Slot called when a socket gets disconnected | ||
| 106 | + void slotDisconnected(); | ||
| 107 | + | ||
| 108 | +signals: | ||
| 109 | + /** | ||
| 110 | + * @brief Signal emitted when data is received | ||
| 111 | + * @param sData Block of data received | ||
| 112 | + * @param pSocket Receiving socket | ||
| 113 | + */ | ||
| 114 | + void signalDataReceived( const QString &sData, TcpSocket *pSocket ); | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * @brief Signal emitted when data was sent | ||
| 118 | + * @param pSocket Sending socket | ||
| 119 | + */ | ||
| 120 | + void signalDataSent( TcpSocket *pSocket ); | ||
| 121 | + | ||
| 122 | + /** | ||
| 123 | + * @brief Signal emitted when data is being received | ||
| 124 | + * @param pSocket Receiving socket | ||
| 125 | + */ | ||
| 126 | + void signalReceivingData( TcpSocket *pSocket ); | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * @brief Signal emitted when a socket gets connected | ||
| 130 | + * @param pSocket Socket that gets connected | ||
| 131 | + */ | ||
| 132 | + void signalConnected( TcpSocket *pSocket ); | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * @brief Signal emitted when a socket gets disconnected | ||
| 136 | + * @param pSocket Socket that gets disconnected | ||
| 137 | + */ | ||
| 138 | + void signalDisconnected( TcpSocket *pSocket ); | ||
| 139 | + | ||
| 140 | +private slots: | ||
| 141 | + /// @brief Slot called when a socket is ready to read | ||
| 142 | + void slotReadyRead(); | ||
| 143 | + | ||
| 144 | + /// @brief Emits the signalDataSent when all data was sent | ||
| 145 | + void chkSendBuffer(); | ||
| 146 | + | ||
| 147 | +private: | ||
| 148 | + /// @brief Connects the socket signals to the slots of this class | ||
| 149 | + void connectSocketSignals(); | ||
| 150 | + | ||
| 151 | + /// @brief Disconnects the socket signals to the slots of this class | ||
| 152 | + void disconnectSocketSignals(); | ||
| 153 | + | ||
| 154 | + QTcpSocket *m_pSocket; ///< Current communication socket | ||
| 155 | + int m_blockSize; ///< Maximum block size | ||
| 156 | + QString m_dataBuffer; ///< Data to be sent | ||
| 157 | + | ||
| 158 | + std::unique_ptr<QTimer> m_pTimer; ///< Periodic check if all data was sent | ||
| 159 | +}; | ||
| 160 | + | ||
| 161 | +} // End namespace components | ||
| 162 | +} // End namespace osdev | ||
| 163 | + | ||
| 164 | +#endif // OSDEV_COMPONENTS_CTCPSOCKET_H |
tests/CMakeLists.txt
0 → 100644
| 1 | +++ a/tests/CMakeLists.txt | ||
| 1 | +cmake_minimum_required(VERSION 3.0) | ||
| 2 | +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake) | ||
| 3 | + | ||
| 4 | +include(projectheader) | ||
| 5 | +project_header(test_logutils) | ||
| 6 | + | ||
| 7 | +include_directories( SYSTEM | ||
| 8 | + ${CMAKE_CURRENT_SOURCE_DIR}/../../src | ||
| 9 | +) | ||
| 10 | + | ||
| 11 | +include(compiler) | ||
| 12 | +set(SRC_LIST | ||
| 13 | +) | ||
| 14 | + | ||
| 15 | +# add_executable( ${PROJECT_NAME} | ||
| 16 | +# ${SRC_LIST} | ||
| 17 | +# ) | ||
| 18 | + | ||
| 19 | +# target_link_libraries( | ||
| 20 | +# ${PROJECT_NAME} | ||
| 21 | +# ) | ||
| 22 | + | ||
| 23 | +# set_target_properties( ${PROJECT_NAME} PROPERTIES | ||
| 24 | +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin | ||
| 25 | +# LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib | ||
| 26 | +# ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/archive | ||
| 27 | +# ) | ||
| 28 | + | ||
| 29 | +# include(installation) | ||
| 30 | +# install_application() |