#include "ptsocket.h" PTSocket::PTSocket(QObject* p) : QTcpSocket(p) { #ifdef PT_DEBUG m_willPing = true; dbgSetWillHandshake(true); #endif setSocketOption(QAbstractSocket::LowDelayOption, 1); m_packetSize = 0; m_timeoutTimer.setSingleShot(true); setTimeout(5000); m_pingTimer.setSingleShot(true); #ifdef PT_DEBUG setPingInterval(1000 * 7); #else setPingInterval(1000 * 60); #endif m_state = Disconnected; m_isServerSide = false; connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(m_timedout())); connect(&m_pingTimer, SIGNAL(timeout()), this, SLOT(m_ping())); connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(m_stateChanged(QAbstractSocket::SocketState))); connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(m_socketError(QAbstractSocket::SocketError))); connect(this, SIGNAL(readyRead()), this, SLOT(m_readyRead())); } PTSocket::State PTSocket::getState() const { return m_state; } int PTSocket::getTimeout() const { return m_timeout; } int PTSocket::getPingInterval() const { return m_pingInterval; } bool PTSocket::isServerSide() const { return m_isServerSide; } QString PTSocket::getError() const { return (m_error.isEmpty()? errorString() : m_error); } QByteArray PTSocket::handshakeData() { return "LibPTSocketv1"; } void PTSocket::setTimeout(int t) { m_timeout = t; if(m_timeoutTimer.isActive()) m_timeoutTimer.start(m_timeout); else m_timeoutTimer.setInterval(m_timeout); } void PTSocket::setPingInterval(int p) { m_pingInterval = p; if(m_pingTimer.isActive()) m_pingTimer.start(m_pingInterval); else m_pingTimer.setInterval(m_pingInterval); } void PTSocket::setIsServerSide(bool s) { m_isServerSide = s; } void PTSocket::send(int pktType, QByteArray data) { send(false, pktType, data); } void PTSocket::disconnectFromServer() { disconnectFromHost(); } void PTSocket::dbgSetWillPing(bool w) { #ifdef PT_DEBUG m_willPing = w; #endif } void PTSocket::dbgSetWillHandshake(bool w) { #ifdef PT_DEBUG m_willHandshake = w; m_willHandshakeFake1 = false; m_willHandshakeFake2 = false; #endif } void PTSocket::dbgSetWillHandshakeFake1(bool w) { #ifdef PT_DEBUG m_willHandshakeFake1 = w; if(!w) m_willHandshake = true; else m_willHandshake = false; m_willHandshakeFake2 = false; #endif } void PTSocket::dbgSetWillHandshakeFake2(bool w) { #ifdef PT_DEBUG m_willHandshakeFake2 = w; if(!w) m_willHandshake = true; else m_willHandshake = false; m_willHandshakeFake1 = false; #endif } void PTSocket::send(bool sys, int pktType, QByteArray data) { if(data.isEmpty()) data = "\0"; data.prepend(intToByteArray(pktType)); data.prepend((char)sys); data.prepend(intToByteArray(data.size())); write(data); } void PTSocket::m_ping() { m_pingTimer.stop(); if(m_timeoutTimer.isActive()) return; #ifdef PT_DEBUG if(m_willPing) send(true, PingRequest); #else send(true, PingRequest); #endif m_timeoutTimer.start(); } void PTSocket::m_readyRead() { m_timeoutTimer.stop(); m_pingTimer.start(); if(m_state == Handshaked) { if(m_packetSize == 0) { if((unsigned)bytesAvailable() >= sizeof(int)) m_packetSize = byteArrayToInt(read(sizeof(int))); else if(bytesAvailable() != 0) m_timeoutTimer.start(); } if(m_packetSize != 0) { if((unsigned)bytesAvailable() >= m_packetSize) { bool sys = (bool)read(1).at(0); QByteArray p = read(sizeof(int)); int pktType = byteArrayToInt(p); QByteArray data = read(m_packetSize - 1 - p.size()); if(sys) { if(pktType == PingRequest) { #ifdef PT_DEBUG if(m_willPing) send(true, PingAnswer); #else send(true, PingAnswer); #endif } //else if(pktType == PingAnswer) } else emit packetReceived(pktType, data); m_packetSize = 0; m_readyRead(); } else m_timeoutTimer.start(); } } else { QByteArray handshake = handshakeData(); if(bytesAvailable() >= handshake.size()) { QByteArray data = read(handshake.size()); if(data == handshake) { setState(Handshaked); m_readyRead(); } else m_handshakeError(); } else m_timeoutTimer.start(); } } void PTSocket::m_timedout() { if(m_state == Disconnected) return; if(m_state == HostLookUp) setError("Unable to find the host " + peerName() + " in the time (" + QString::number(m_timeout) + " ms)"); else if(m_state == Connecting) setError("The " + QString(isServerSide()? "client " + peerAddress().toString() : "server " + peerName()) + " didn't answer to the connection request in the time (" + QString::number(m_timeout) + " ms)"); else if(m_state == Connected) setError("The " + QString(isServerSide()? "client " + peerAddress().toString() : "server " + peerName()) + " didn't answer to the handshake in the time (" + QString::number(m_timeout) + " ms)"); else if(m_state == Handshaked) setError("The " + QString(isServerSide()? "client " + peerAddress().toString() : "server " + peerName()) + " timed out (" + QString::number(m_timeout) + " ms)"); else if(m_state == Disconnecting) abort(); } void PTSocket::m_handshakeError() { setError("The " + QString(isServerSide()? "client " + peerAddress().toString() : "server " + peerName()) + " didn't answer to the handshake correclty"); } void PTSocket::m_stateChanged(QAbstractSocket::SocketState s) { if(s == QAbstractSocket::HostLookupState) setState(HostLookUp); else if(s == QAbstractSocket::ConnectingState) setState(Connecting); else if(s == QAbstractSocket::ConnectedState) { m_packetSize = 0; setState(Connected); #ifdef PT_DEBUG if(m_willHandshakeFake1) write("LibPTSocketv"); else if(m_willHandshakeFake2) write("42LibPTSocketv4242"); else if(m_willHandshake) write(handshakeData()); #else write(handshakeData()); #endif } else if(s == QAbstractSocket::ClosingState) setState(Disconnecting); m_timeoutTimer.start(); if(s == QAbstractSocket::UnconnectedState) { if(error() == QAbstractSocket::UnknownSocketError || error() == QAbstractSocket::RemoteHostClosedError) { m_timeoutTimer.stop(); setState(Disconnected); } } } void PTSocket::m_socketError(QAbstractSocket::SocketError s) { if(s != QAbstractSocket::RemoteHostClosedError) setState(Error); } void PTSocket::setState(PTSocket::State s) { if(s == m_state) return; if(s == Error && (m_state == Disconnected || m_state == Disconnecting)) return; if(m_state == Error && (s == Disconnected || s == Disconnecting)) return; m_state = s; if(s == Error) abort(); else m_error.clear(); if(s == Handshaked) m_pingTimer.start(); else m_pingTimer.stop(); emit stateChanged(m_state); } void PTSocket::setError(QString e) { if(m_state == Error || m_state == Disconnected || m_state == Disconnecting) return; m_error = e; setState(Error); } QByteArray PTSocket::intToByteArray(int a) { QByteArray data; for(unsigned char i = 0; i < sizeof(a); ++i) data.append((char)(a >> 8 * i)); return data; } int PTSocket::byteArrayToInt(QByteArray d) { int a = 0; for(unsigned char i = 0; i < d.size(); ++i) a |= (unsigned int)((unsigned char)d.at(i) << i * 8); return a; } QDebug operator <<(QDebug dbg, const PTSocket::State s) { const QMetaObject & mo = PTSocket::staticMetaObject; QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("State")); return dbg<