tcpinterface.cpp 5.73 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/

#include "tcpinterface.h"
#include "log.h"

#include <QHostAddress>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDataStream>

namespace osdev  {
namespace components {

TcpInterface::TcpInterface(const QString& hostName, int port, bool i_bServer)
    : m_bServer( i_bServer ),
      m_active(false),
      m_tcpServer( nullptr ),
      m_tcpSocket( nullptr ),
      m_clientConnection( nullptr ),
      m_blockSize( 0 ),
      m_dataList()
{
    QHostAddress hostAddress(hostName);

    if (m_bServer)
    {   // This interface is a server
        m_tcpServer = new QTcpServer(this);
        connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
        m_active = m_tcpServer->listen(hostAddress, static_cast<quint16>(port));
        if (!m_active)
        {
            LogWarning("interfaceplugin",
                       "Unable to start the server: " + m_tcpServer->errorString());
        }
    }
    else
    {   // This interface is a client
        m_tcpSocket = new QTcpSocket(this);
        m_tcpSocket->connectToHost(hostAddress, static_cast<quint16>(port));
        m_active = m_tcpSocket->waitForConnected(1000);
        if (m_active)
        {
            connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readData()));
        }
    }
}


TcpInterface::~TcpInterface()
{
}

QString TcpInterface::getData()
{
    QString qsResult;
    if(!m_dataList.isEmpty())
    {
        qsResult = m_dataList.takeFirst();
    }
    return qsResult;
}

void TcpInterface::readData()
{
    QTcpSocket* pSocket = nullptr;
    /* Retrieve the correct Socket, depending on connection-type */
    if (m_bServer)
    {
        pSocket = m_clientConnection;
    }
    else
    {
        pSocket = m_tcpSocket;
    }

    /* Construct a DataStream from the Socket */
    QDataStream in(pSocket);
    in.setVersion(QDataStream::Qt_5_4);

    // Process while there is a following message in this same buffer.
    do {
        LogDebug("TcpInterface", "read bytes");
        /* The size of the message in bytes is required */
        if (m_blockSize == 0)
        {
            /*
             * The size of the message should be available as the first four
             * blocks of the message; i.e. (sizeof(quint32))
             */
            if (pSocket->bytesAvailable() < static_cast<int>(sizeof(quint32)))
            {
                return;
            }
            /* Retrieve the blocksize if it is fully available from the socket */
            in >> m_blockSize;
            LogDebug("TcpInterface", QString("block size is %1").arg(m_blockSize));
        }

        if (pSocket->bytesAvailable() < m_blockSize)
        {
            return;
        }

        /*
         * Retrieve the message if it is completely available from the socket.
         * I.e.: The number of available bytes must be at least the retrieved
         * message-size.
         */
        QByteArray pDataStore;
        /*
         * Using an empty QByteArray the operator>> can be used, because it then
         * reads in everything until the first '\0' character.
         * It is then required that all messages are ended by a '\0'. This is
         * standard when sending QByteArrays.
         */
        in >> pDataStore;
        /*
         * An entire message is stored in a new QByteArray and must be pushed on
         * the stack of available messages
         */
        m_dataList.append( pDataStore );
        m_blockSize = 0; // reset blocksize for next read
        emit dataPresent();
    } while (pSocket->bytesAvailable() > 0);
}

void TcpInterface::sendData(const QString& i_qsData)
{
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);

    out.setVersion(QDataStream::Qt_5_4);

    out << block.size();
    out << i_qsData.toLatin1();

    if (m_bServer)
    {
        m_clientConnection->write(block);
        m_clientConnection->flush();
    }
    else
    {
        m_tcpSocket->write(block);
        m_tcpSocket->flush();
    }
}

void TcpInterface::newConnection()
{
    m_clientConnection = m_tcpServer->nextPendingConnection();
    connect(m_clientConnection, SIGNAL(readyRead()), this, SLOT(readData()));
}

}
}