/* * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include "bsocket.h" //#include "safedelete.h" #ifndef NO_NDNS #include "ndns.h" #endif #include "srvresolver.h" //#define BS_DEBUG #ifdef BS_DEBUG #include #endif #define READBUFSIZE 65536 // CS_NAMESPACE_BEGIN #include "psilogger.h" class QTcpSocketSignalRelay : public QObject { Q_OBJECT public: QTcpSocketSignalRelay(QTcpSocket *sock, QObject *parent = 0) :QObject(parent) { qRegisterMetaType("QAbstractSocket::SocketError"); connect(sock, SIGNAL(hostFound()), SLOT(sock_hostFound()), Qt::QueuedConnection); connect(sock, SIGNAL(connected()), SLOT(sock_connected()), Qt::QueuedConnection); connect(sock, SIGNAL(disconnected()), SLOT(sock_disconnected()), Qt::QueuedConnection); connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()), Qt::QueuedConnection); connect(sock, SIGNAL(bytesWritten(qint64)), SLOT(sock_bytesWritten(qint64)), Qt::QueuedConnection); connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError)), Qt::QueuedConnection); } signals: void hostFound(); void connected(); void disconnected(); void readyRead(); void bytesWritten(qint64); void error(QAbstractSocket::SocketError); public slots: void sock_hostFound() { emit hostFound(); } void sock_connected() { emit connected(); } void sock_disconnected() { emit disconnected(); } void sock_readyRead() { emit readyRead(); } void sock_bytesWritten(qint64 x) { emit bytesWritten(x); } void sock_error(QAbstractSocket::SocketError x) { emit error(x); } }; class BSocket::Private { public: Private() { qsock = 0; qsock_relay = 0; } QTcpSocket *qsock; QTcpSocketSignalRelay *qsock_relay; int state; #ifndef NO_NDNS NDns ndns; #endif SrvResolver srv; QString host; int port; //SafeDelete sd; }; BSocket::BSocket(QObject *parent) :ByteStream(parent) { d = new Private; #ifndef NO_NDNS connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done())); #endif connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done())); reset(); } BSocket::~BSocket() { reset(true); delete d; PsiLogger::instance()->log(QString("%1 BSocket::~BSocket()").arg(LOG_THIS)); } void BSocket::reset(bool clear) { PsiLogger::instance()->log(QString("%1 BSocket::reset()").arg(LOG_THIS)); if(d->qsock) { delete d->qsock_relay; d->qsock_relay = 0; /*d->qsock->disconnect(this); if(!clear && d->qsock->isOpen() && d->qsock->isValid()) {*/ // move remaining into the local queue QByteArray block(d->qsock->bytesAvailable(), 0); d->qsock->read(block.data(), block.size()); appendRead(block); //} //d->sd.deleteLater(d->qsock); // delete d->qsock; d->qsock->deleteLater(); d->qsock = 0; } else { if(clear) clearReadBuffer(); } if(d->srv.isBusy()) d->srv.stop(); #ifndef NO_NDNS if(d->ndns.isBusy()) d->ndns.stop(); #endif d->state = Idle; } void BSocket::ensureSocket() { PsiLogger::instance()->log(QString("%1 BSocket::ensureSocket()").arg(LOG_THIS)); if(!d->qsock) { d->qsock = new QTcpSocket; #if QT_VERSION >= 0x030200 d->qsock->setReadBufferSize(READBUFSIZE); #endif d->qsock_relay = new QTcpSocketSignalRelay(d->qsock); connect(d->qsock_relay, SIGNAL(hostFound()), SLOT(qs_hostFound())); connect(d->qsock_relay, SIGNAL(connected()), SLOT(qs_connected())); connect(d->qsock_relay, SIGNAL(disconnected()), SLOT(qs_closed())); connect(d->qsock_relay, SIGNAL(readyRead()), SLOT(qs_readyRead())); connect(d->qsock_relay, SIGNAL(bytesWritten(qint64)), SLOT(qs_bytesWritten(qint64))); connect(d->qsock_relay, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(qs_error(QAbstractSocket::SocketError))); } } void BSocket::connectToHost(const QString &host, quint16 port) { PsiLogger::instance()->log(QString("%1 BSocket::connectToHost(%2, %3)").arg(LOG_THIS).arg(host).arg(port)); reset(true); d->host = host; d->port = port; #ifdef NO_NDNS d->state = Connecting; do_connect(); #else d->state = HostLookup; d->ndns.resolve(d->host); #endif } void BSocket::connectToServer(const QString &srv, const QString &type) { PsiLogger::instance()->log(QString("%1 BSocket::connectToServer(%2, %3)").arg(LOG_THIS).arg(srv).arg(type)); reset(true); d->state = HostLookup; d->srv.resolve(srv, type, "tcp"); } int BSocket::socket() const { if(d->qsock) return d->qsock->socketDescriptor(); else return -1; } void BSocket::setSocket(int s) { PsiLogger::instance()->log(QString("%1 BSocket::setSocket(%2)").arg(LOG_THIS).arg(s)); reset(true); ensureSocket(); d->state = Connected; d->qsock->setSocketDescriptor(s); } int BSocket::state() const { return d->state; } bool BSocket::isOpen() const { if(d->state == Connected) return true; else return false; } void BSocket::close() { PsiLogger::instance()->log(QString("%1 BSocket::close()").arg(LOG_THIS)); if(d->state == Idle) return; if(d->qsock) { d->qsock->close(); d->state = Closing; if(d->qsock->bytesToWrite() == 0) reset(); } else { reset(); } } void BSocket::write(const QByteArray &a) { if(d->state != Connected) return; #ifdef BS_DEBUG QString s = QString::fromUtf8(a); fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), s.latin1()); #endif d->qsock->write(a.data(), a.size()); } QByteArray BSocket::read(int bytes) { QByteArray block; if(d->qsock) { int max = bytesAvailable(); if(bytes <= 0 || bytes > max) bytes = max; block.resize(bytes); d->qsock->read(block.data(), block.size()); } else block = ByteStream::read(bytes); #ifdef BS_DEBUG QString s = QString::fromUtf8(block); fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1()); #endif return block; } int BSocket::bytesAvailable() const { if(d->qsock) return d->qsock->bytesAvailable(); else return ByteStream::bytesAvailable(); } int BSocket::bytesToWrite() const { if(!d->qsock) return 0; return d->qsock->bytesToWrite(); } QHostAddress BSocket::address() const { if(d->qsock) return d->qsock->localAddress(); else return QHostAddress(); } quint16 BSocket::port() const { if(d->qsock) return d->qsock->localPort(); else return 0; } QHostAddress BSocket::peerAddress() const { if(d->qsock) return d->qsock->peerAddress(); else return QHostAddress(); } quint16 BSocket::peerPort() const { if(d->qsock) return d->qsock->peerPort(); else return 0; } void BSocket::srv_done() { PsiLogger::instance()->log(QString("%1 BSocket::srv_done()").arg(LOG_THIS)); if(d->srv.failed()) { #ifdef BS_DEBUG fprintf(stderr, "BSocket: Error resolving hostname.\n"); #endif error(ErrHostNotFound); return; } d->host = d->srv.resultAddress().toString(); d->port = d->srv.resultPort(); do_connect(); //QTimer::singleShot(0, this, SLOT(do_connect())); //hostFound(); } void BSocket::ndns_done() { PsiLogger::instance()->log(QString("%1 BSocket::ndns_done()").arg(LOG_THIS)); #ifndef NO_NDNS if(!d->ndns.result().isNull()) { d->host = d->ndns.resultString(); d->state = Connecting; do_connect(); //QTimer::singleShot(0, this, SLOT(do_connect())); //hostFound(); } else { #ifdef BS_DEBUG fprintf(stderr, "BSocket: Error resolving hostname.\n"); #endif error(ErrHostNotFound); } #endif } void BSocket::do_connect() { PsiLogger::instance()->log(QString("%1 BSocket::do_connect()").arg(LOG_THIS)); #ifdef BS_DEBUG fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port); #endif ensureSocket(); d->qsock->connectToHost(d->host, d->port); } void BSocket::qs_hostFound() { PsiLogger::instance()->log(QString("%1 BSocket::qs_hostFound()").arg(LOG_THIS)); //SafeDeleteLock s(&d->sd); } void BSocket::qs_connected() { PsiLogger::instance()->log(QString("%1 BSocket::qs_connected()").arg(LOG_THIS)); d->state = Connected; #ifdef BS_DEBUG fprintf(stderr, "BSocket: Connected.\n"); #endif //SafeDeleteLock s(&d->sd); connected(); } void BSocket::qs_closed() { PsiLogger::instance()->log(QString("%1 BSocket::qs_closed()").arg(LOG_THIS)); if(d->state == Closing) { #ifdef BS_DEBUG fprintf(stderr, "BSocket: Delayed Close Finished.\n"); #endif //SafeDeleteLock s(&d->sd); reset(); delayedCloseFinished(); } } void BSocket::qs_readyRead() { //SafeDeleteLock s(&d->sd); readyRead(); } void BSocket::qs_bytesWritten(qint64 x64) { int x = x64; #ifdef BS_DEBUG fprintf(stderr, "BSocket: BytesWritten [%d].\n", x); #endif //SafeDeleteLock s(&d->sd); bytesWritten(x); } void BSocket::qs_error(QAbstractSocket::SocketError x) { PsiLogger::instance()->log(QString("%1 BSocket::qs_error(%2)").arg(LOG_THIS).arg(x)); if(x == QTcpSocket::RemoteHostClosedError) { #ifdef BS_DEBUG fprintf(stderr, "BSocket: Connection Closed.\n"); #endif //SafeDeleteLock s(&d->sd); reset(); connectionClosed(); return; } #ifdef BS_DEBUG fprintf(stderr, "BSocket: Error.\n"); #endif //SafeDeleteLock s(&d->sd); // connection error during SRV host connect? try next if(d->state == HostLookup && (x == QTcpSocket::ConnectionRefusedError || x == QTcpSocket::HostNotFoundError)) { d->srv.next(); return; } reset(); if(x == QTcpSocket::ConnectionRefusedError) error(ErrConnectionRefused); else if(x == QTcpSocket::HostNotFoundError) error(ErrHostNotFound); else error(ErrRead); } #include "bsocket.moc" // CS_NAMESPACE_END