/** * TcpSocket handling for MeteoDataUnit * $Id$ * $HeadURL$ * $Author$ * Created by hjl, SLSE, 2016-09-22 */ #include "tcpsocket.h" #include #include //#include // used for keepalive //#include // used for keepalive //#include // used for keepalive namespace MeteoDataManagement { TcpSocket::TcpSocket(const char *logName, QObject *parent) : QObject(parent) , m_waitTime(1) , m_socket(nullptr) , m_tryReconnect(false) , m_bSocketErrorWarnedOnce(false) , m_logger(getLogger(logName)) { DEBUG_LOG(m_logger) << "CTOR TCP-Socket... " ; doConnect(); } //------------------------------------------------------------------- TcpSocket::~TcpSocket() { if (m_socket) m_socket->close(); delete m_socket; } //------------------------------------------------------------------- void TcpSocket::doConnect() { DEBUG_LOG(m_logger) << "doConnect TCP-Socket " ; if (m_socket) m_socket->deleteLater(); m_socket = new QTcpSocket(this); connect(m_socket, &QTcpSocket::connected, this, &TcpSocket::onConnected); connect(m_socket, &QTcpSocket::disconnected, this, &TcpSocket::onDisonnected); connect(m_socket, QOverload::of(&QTcpSocket::error), this, &TcpSocket::onError); connect(m_socket, &QTcpSocket::readyRead, this, &TcpSocket::onReadyRead); if (m_tryReconnect) { INFO_LOG(m_logger) << "Try reconnect to host " << m_hostName << ":" << m_hostPort; m_socket->connectToHost(m_hostName, m_hostPort); } } //------------------------------------------------------------------- void TcpSocket::start(const QString &hostName, quint16 port) { INFO_LOG(m_logger) << "Try connect to host: " << hostName << ": " << port; m_hostName = hostName; m_hostPort = port; m_socket->connectToHost(m_hostName, port); } //------------------------------------------------------------------- void TcpSocket::onConnected() { INFO_LOG(m_logger) << "Connected to " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort(); m_bSocketErrorWarnedOnce = false; m_waitTime = 1; // Bei einer TCP Verbindung ergibt sich ein Problem bei der Pruefung ob die Verbindung noch besteht. // Das sollte mit Hilfe des 'TCP keepalive Mechanismus' behoben werden. // Infos dazu u.A. unter http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#intro // Leider fuehrte keine der Varianten zum Erfolg. // Der Abbruch wurde unabhaengig der Parameter in allen Faellen immer nach ziemlich genau 15:40Min dedektiert. // Das Setzen der Systemvariablen war ebenfalls ohne Auswirkung. // Da diese Programmteile unter Windows eh so nicht zu uebersetzen sind, ist der Codeteil hier in Kommentar gesetzt. // INFO_LOG(m_logger) << "----Set TCP_KEEPIDLE parameter "; // const int keepAliveOption = 1; // on // if (setsockopt(m_socket->socketDescriptor(), SOL_SOCKET, SO_KEEPALIVE, &keepAliveOption, sizeof(keepAliveOption)) != 0) // { // ERROR_LOG(m_logger) << "Error setting SO_KEEPALIVE: " << errno; // } // const int idleTimeout = 5; // sec // if (setsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPIDLE, &idleTimeout, sizeof(idleTimeout)) != 0) // { // ERROR_LOG(m_logger) << "Error setting TCP_KEEPIDLE: " << errno; // } // const int keepIntervalCnt = 10; // sec // if (setsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPINTVL, &keepIntervalCnt, sizeof(keepIntervalCnt)) != 0) // { // ERROR_LOG(m_logger) << "Error setting TCP_KEEPINTVL: " << errno; // } // const int idlePaketCnt = 2; // 2 probes -> detection shoud be comes after 5s + 2 * 10s => 25 sec // if (setsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPCNT, &idlePaketCnt, sizeof(idlePaketCnt)) != 0) // { // ERROR_LOG(m_logger) << "Error setting TCP_KEEPCNT: " << errno; // } // //----- Testausgaben // int getOption = 0; // socklen_t optlen = sizeof(getOption); // getsockopt(m_socket->socketDescriptor(), SOL_SOCKET, SO_KEEPALIVE, &getOption, &optlen); // ERROR_LOG(m_logger) << "...SO_KEEPALIVE set to: " << getOption; // getsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPIDLE, &getOption, &optlen); // ERROR_LOG(m_logger) << "...TCP_KEEPIDLE set to: " << getOption; // getsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPINTVL, &getOption, &optlen); // ERROR_LOG(m_logger) << "...TCP_KEEPINTVL set to: " << getOption; // getsockopt(m_socket->socketDescriptor(), SOL_TCP, TCP_KEEPCNT, &getOption, &optlen); // ERROR_LOG(m_logger) << "...TCP_KEEPCNT set to: " << getOption; init(); // pure virtual function // Auspraegung mit spezieller Sensorinitialisierung durchfuehren } //------------------------------------------------------------------- void TcpSocket::onDisonnected() { INFO_LOG(m_logger) << "Disconnected from " << m_socket->peerAddress().toString() << ":" << m_socket->peerPort(); } //------------------------------------------------------------------- void TcpSocket::onError(QAbstractSocket::SocketError err) { ERROR_LOG(m_logger) << "Socket error: " << err << " \"" << m_socket->errorString() << "\"" << " Hostname: " << m_hostName << " Serial Port:" << m_hostPort << " - trying to reconnect in " << m_waitTime << " seconds"; m_socket->disconnectFromHost(); // try reconnects .... m_tryReconnect = true; if (m_socket->state() != QAbstractSocket::UnconnectedState) m_socket->waitForDisconnected(); m_waitTime += 2; if (m_waitTime > 120) m_waitTime = 120; QTimer::singleShot(m_waitTime * 1000, this, &TcpSocket::doConnect); } //------------------------------------------------------------------- void TcpSocket::onReadyRead() { // zeilenweises Einlesen der Sensordaten while (m_socket->canReadLine()) { const QByteArray line = m_socket->readLine(); DEBUG_LOG(m_logger) << "Read line from sensor: " << line; dataAvailable(line); // pure virtual function // Auspraegung mit spezieller Sensordatenauswertung durchfuehren } } //------------------------------------------------------------------- bool TcpSocket::sendCommand(const QByteArray &qba) { bool bOk = true; INFO_LOG(m_logger) << "Send command: " << qba; if (m_socket->state() != QTcpSocket::ConnectedState) { if (m_bSocketErrorWarnedOnce) { m_bSocketErrorWarnedOnce = true; WARNING_LOG(m_logger) << "Error on sendCommand(). No socket connection estabilished." << " Hostname: " << m_hostName; } return false; } int trxBytes = m_socket->write(qba); if (trxBytes != qba.size()) { bOk = false; ERROR_LOG(m_logger) << "Insufficient number of tansfered bytes." << " Soll: " << trxBytes << " Ist: " << qba.size(); } m_socket->flush(); return bOk; } //------------------------------------------------------------------- } // namespace MeteoDataManagement