474 lines
10 KiB
C++
474 lines
10 KiB
C++
/*
|
|
* 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 <QTcpSocket>
|
|
#include <QHostAddress>
|
|
#include <QMetaType>
|
|
|
|
#include "bsocket.h"
|
|
|
|
//#include "safedelete.h"
|
|
#ifndef NO_NDNS
|
|
#include "ndns.h"
|
|
#endif
|
|
#include "srvresolver.h"
|
|
|
|
//#define BS_DEBUG
|
|
|
|
#ifdef BS_DEBUG
|
|
#include <stdio.h>
|
|
#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>("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
|