Задача такая - реализовать маленькую проксю. Без авторизации и прочих плюшек, просто чтобы соединяла браузер и удаленный сервер.
Разумеется каждое подключение должно работать в отдельном потоке.
Итак, вот как я сделал примерно после 20-го переписывания кода.
Когда приходит событие о новом подключении(от QTcpServer), я создаю новый экземпляр класса(Transfer), который будет создавать поток(TransferThread : QThread), и передаю в этот класс ID сокета, через который работает подключение. Из run я создаю экземпляр класса TransferHandler и вызываю exec() чтобы запустить eventloop.
В конструкторе TransferHandler я создаю объект сокета, и связываю его с тем ID сокета, который был получен из события (этот сокет назван in).
Кое-что делаю с HTTP заголовком.
Создаю новый сокет на удаленный сервер. (назван out)
Если все хорошо то отправляю на out заголовок и данные полученные к этому моменту из in.
Делаю соответствующие коннекты для readyRead из in и out, и еще disconnected
Когда происходит событие readyRead в in, я копирую всё из in в out.
Когда происходит событие readyRead в out, я копирую всё из out в in.
Когда на одном из сокетов происходит дисконнект я удаляю оба сокета, убиваю поток.
Проблема: Ответы на все запросы полностью приходят с сервера, однако в браузере появляется только часть из них (видимо только первый пакет), а далее как будто браузер зависает (то есть он все время остается в статусе загрузки(бегающий кружечек почти во всех браузерах), хотя данные уже ушли).
Исходный код:
#ifndef TRANSFER_H
#define TRANSFER_H
#include <QString>
#include "RequestHeader.h"
#include "Server.h"
#include <QTcpSocket>
#include <QByteArray>
#include <QThread>
#include "stateprovider.h"
#include "browserinfo.h"
#include "BancutApp.h"
#include <QStringList>
#include <QFile>
class CCore;
class TcpSocket: public QTcpSocket // на самом деле наверное можно было и без этого класса обойтись, но тут раньше был дополнительный слот, который я больше не использую
{
Q_OBJECT
public:
TcpSocket();
};
class TransferHandler: public QObject
{
Q_OBJECT
TcpSocket * in;
TcpSocket * out;
CCore * core;
StateProvider * state;
QFile * file;
public:
TransferHandler(int socketHandler);
~TransferHandler() {file->close();}
void closeAll();
signals:
void finished();
void sendIn(QByteArray);
void sendOut(QByteArray);
public slots:
void inReadyRead();
void outReadyRead();
void inDisconnected();
void outDisconnected();
};
class TransferThread: public QThread
{
Q_OBJECT
int handle;
public:
TransferThread(int handle);
void run();
signals:
void sendOut(QByteArray data);
void sendIn(QByteArray data);
public slots:
void finishedEvent();
};
class Transfer : public QObject
{
Q_OBJECT
TransferThread * thread;
public:
Transfer(int socketHandle);
protected slots:
void threadFinished();
};
#endif // TRANSFER_H
#include "Transfer.h"
#include "Utils.h"
#include "Core.h"
#include "FilterManager.h"
TcpSocket::TcpSocket()
: QTcpSocket()
{
}
TransferHandler::TransferHandler(int socketHandle)
{
core = &CCore::Instance();
state = StateProvider::getInstance();
file = new QFile(QString("%1_%2.in").arg((long)in).arg(qrand()));
file->open(QFile::Append);
in = new TcpSocket;
in->setSocketDescriptor(socketHandle);
if(!in->isValid()) return;
if(!in->waitForReadyRead()) return;
QByteArray request = in->readAll();
if(request.isEmpty())
{
qDebug() << "Empty request";
return;
}
CRequestHeader header;
QByteArray pack(300*1024, 'p');
header.Extract(&request, &pack);
header.Remove("Keep-Alive");
header.Remove("Proxy-Connection");
QString strHead = header.Build();
QString host = header.GetServerName();
out = new TcpSocket;
out->connectToHost(GetServerName(host), GetPort(host));
if (!out->waitForConnected())
{
in->write(out->errorString().toLatin1());
in->waitForBytesWritten();
qDebug() << "host invalid:" << out->errorString();
return;
}
out->write(strHead.toLatin1());
out->write(pack);
connect(in, SIGNAL(disconnected()), SLOT(inDisconnected()));
connect(out, SIGNAL(disconnected()), SLOT(outDisconnected()));
connect(in, SIGNAL(readyRead()), SLOT(inReadyRead()));
connect(out, SIGNAL(readyRead()), SLOT(outReadyRead()));
}
void TransferHandler::closeAll()
{
if(in && in->isValid() && in->state() == QAbstractSocket::ConnectedState) return;
if(out && out->isValid() && out->state() == QAbstractSocket::ConnectedState) return;
in->close();
out->close();
emit finished();
}
void TransferHandler::inReadyRead()
{
qDebug() << "inReadyRead()";
if (in->isValid() && out->isValid())
{
if (in->state() == QAbstractSocket::ConnectedState && out->state() == QAbstractSocket::ConnectedState )
{
in->waitForReadyRead();
QByteArray data = in->readAll();
if (!data.isEmpty())
{
out->write(data);
}
}
}
}
void TransferHandler::outReadyRead()
{
qDebug() << "outReadyRead()";
if (in->isValid() && out->isValid())
{
if (in->state() == QAbstractSocket::ConnectedState && out->state() == QAbstractSocket::ConnectedState )
{
out->waitForReadyRead();
QByteArray data = out->readAll();
if (!data.isEmpty())
{
in->write(data);
file->write(data); // здесь я делаю вывод в файл, просто чтобы проверить что странички грузятся полностью
}
}
}
}
void TransferHandler::inDisconnected()
{
qDebug() << "inDisconnected()";
closeAll();
}
void TransferHandler::outDisconnected()
{
qDebug() << "outDisconnected()";
closeAll();
}
TransferThread::TransferThread(int handle) :
QThread()
{
this->handle = handle;
}
void TransferThread::run()
{
TransferHandler * th = new TransferHandler(handle);
connect(th, SIGNAL(finished()), SLOT(finishedEvent()));
exec();
qDebug() << "Leave thread. deleting TransferHandler";
delete th;
}
void TransferThread::finishedEvent()
{
qDebug() << "Thread finishedEvent()";
exit();
}
Transfer::Transfer(int socketHandle)
{
thread = new TransferThread(socketHandle);
connect(thread, SIGNAL(finished()), SLOT(threadFinished()));
thread->start();
}
void Transfer::threadFinished()
{
delete thread;
delete this;
}