Russian Qt Forum
Сентябрь 27, 2024, 07:15 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Реализация небольшой прокси на двух QTcpSocket  (Прочитано 3977 раз)
DmT
Гость
« : Апрель 08, 2011, 20:41 »

Задача такая - реализовать маленькую проксю. Без авторизации и прочих плюшек, просто чтобы соединяла браузер и удаленный сервер.
Разумеется каждое подключение должно работать в отдельном потоке.
Итак, вот как я сделал примерно после 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;
}
Записан
DmT
Гость
« Ответ #1 : Апрель 09, 2011, 07:20 »

Заметил фишку. Если отказаться от ивентов и делать всю обработку в одном цикле и убрать вызов exec() из run то начинает грузиться нормально. Только очень большой пинг.
код обработчика в цикле:
Код:
    // cycle tranfering packages server <==> browser
    CAnswerHeader answerHead;
    out->waitForReadyRead();
    QByteArray answer = out->readAll();
    in->write(answer);
    answerHead.Extract(&answer, &data);
    int contentLength = answerHead.GetInt("Content-Length");
    int transfered = data.size(); // need to calc only data, without header, but send within it.
    bool inDone = true;
    bool outDone = true;

    // версия 11 рабочая
    do
    {
        inDone = true;
        outDone = true;
        // from browser to remote server
        if (in->waitForReadyRead(1000)) //in.state() != QAbstractSocket::UnconnectedState &&
        {
            pack = in->readAll();
            //if (!pack.isEmpty())
            {
                inDone = false;
                out->write(pack);
            }
        }

        // from remote server to browser
        if (contentLength > -1) // if we exactly know how much is the data size ...
        {
            if (transfered < contentLength) // if data was downloaded not fully yet
            {
                if (out->waitForReadyRead()) //out.state() != QAbstractSocket::UnconnectedState &&
                {
                    data = out->readAll();
                    //if (!data.isEmpty())
                    {
                        outDone = false;
                        in->write(data);
                        transfered += data.size();
                    }
                }
            }
        }
        else if (out->waitForReadyRead())  // if we dont know how much is the data size ... //out.state() != QAbstractSocket::UnconnectedState &&
        {
            data = out->readAll();
            //if (!data.isEmpty())
            {
                outDone = false;
                in->write(data);
                in->flush();
            }
        }
    } while (!inDone || !outDone);
Записан
johnpion
Гость
« Ответ #2 : Август 04, 2012, 12:19 »

Решилась ли проблема? Как вы меряли пинг?
Записан
DmitryM
Гость
« Ответ #3 : Август 04, 2012, 19:38 »

Я проксю на erlang'e писал бы.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.123 секунд. Запросов: 21.