#include "ptsocket.h" PTSocket::PTSocket(QObject* p) : QTcpSocket(p) { setSocketOption(QAbstractSocket::LowDelayOption, 1); m_packetSize = 0; setTimeout(5000); setPingInterval(1000 * 7); 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())); } int PTSocket::getTimeout() const { return m_timeout; } int PTSocket::getPingInterval() const { return m_pingInterval; } bool PTSocket::isServerSide() const { return m_isServerSide; } 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::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; send(true, PingRequest); 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) send(true, PingAnswer); //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); write(handshakeData()); } else if(s == QAbstractSocket::ClosingState) setState(Disconnecting); m_timeoutTimer.start(); if(s == QAbstractSocket::UnconnectedState) { m_timeoutTimer.stop(); setState(Disconnected); } } void PTSocket::m_socketError(QAbstractSocket::SocketError) { setState(Error); } void PTSocket::setState(PTSocket::State s) { if(s == m_state) return; if(s == Error) abort(); if(s == Handshaked) m_pingTimer.start(); else m_pingTimer.stop(); if(m_state == Error && (s != HostLookUp && s != Connecting)) return; m_state = s; emit stateChanged(m_state); } void PTSocket::setError(QString e) { if(m_state == Error) return; setErrorString(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; }