Russian Qt Forum

Qt => Работа с сетью => Тема начата: ManOfOrange от Март 28, 2011, 16:09



Название: Передача файлов через QTcpSocket используя QThread
Отправлено: ManOfOrange от Март 28, 2011, 16:09
Приветствую всех!

Подозреваю, что вопрос один из стандартных, но я не смог найти ответ.

Если клиенты и сервер. Клиенты передают файлы через QTcpSocket. Взял образец от Александра (кстати, большое ему спасибо!) - вот этот: http://www.prog.org.ru/index.php?topic=16260.msg108026#msg108026 (http://www.prog.org.ru/index.php?topic=16260.msg108026#msg108026)
Всё, в общем-то прекрасно работает.

Возникла же следующая задача: чтобы от каждого клиента файлы принимались в отдельном потоке. За образец взял экзампловский классический threaded fortune server, к которому прикрутил вышеупомянутый код от Александра. В итоге получилось следующее:

Клиентская часть (так как проблем с ней нет, всё пишется правильно, то только минимально необходимый кусок):

Код:
void QLANF::onConnected()
{
    QByteArray block;
    QDataStream stream(&block, QIODevice::WriteOnly);
    stream.setVersion(QDataStream::Qt_4_5);

    QFile file(outPath);
    file.open(QIODevice::ReadOnly);
    QByteArray buf = file.readAll();
    stream << quint64(file.size());
    stream << CRC32(outPath);
    stream << buf;
    //qDebug () << "block.size: " << block.size();
    fSocket->write(block);
    fSocket->flush();
}

Серверная часть (а тут сложности). Код был минимально изменён по сравнению с экзамплом.

fortuneserver.h
Код:
#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H

#include <QStringList>
#include <QTcpServer>

//! [0]
class FortuneServer : public QTcpServer
{
    Q_OBJECT

public:
    FortuneServer(QObject *parent = 0);

protected:
    void incomingConnection(int socketDescriptor);

private:
    QStringList fortunes;
};
//! [0]

#endif

fortuneserver.cpp
Код:
#include "fortuneserver.h"
#include "fortunethread.h"

#include <stdlib.h>

//! [0]
FortuneServer::FortuneServer(QObject *parent)
    : QTcpServer(parent), addrreess("C://Qt/outFirst.txt")
{
    /* объявление fortune - ненужное из экзампла*/
}
//! [0]

//! [1]
void FortuneServer::incomingConnection(int socketDescriptor)
{
    QString fortune = fortunes.at(qrand() % fortunes.size());
    FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    thread->start();
}
//! [1]

fortunethread.h
Код:
#ifndef FORTUNETHREAD_H
#define FORTUNETHREAD_H

#include <QThread>
#include <QTcpSocket>

//! [0]
class FortuneThread : public QThread
{
    Q_OBJECT

public:
    FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent);

    void run();

signals:
    void error(QTcpSocket::SocketError socketError);

private:
    int socketDescriptor;
    QString text;
    QString inPath;
    quint64 nextBlockSize;
};
//! [0]

#endif

fortunethread.cpp
Код:
#include "fortunethread.h"

#include <QtNetwork>

//! [0]
FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent)
    : QThread(parent), socketDescriptor(socketDescriptor), text(fortune),inPath("C://Qt/outFirst.txt")
{
    nextBlockSize = 0;
}
//! [0]

//! [1]
void FortuneThread::run()
{
    QTcpSocket tcpSocket;// = static_cast<QTcpSocket*>(sender());

    connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(run()));
//! [1] //! [2]
    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket.error());
        return;
    }
//! [2] //! [3]

//    qDebug () << "In Thread avail: " << tcpSocket.bytesAvailable();
    tcpSocket.waitForReadyRead();
//    qDebug () << "In Thread avail2: " << tcpSocket.bytesAvailable();

    //nextBlockSize = 0;

    forever
    {
        //qDebug () << "In Thread avail3: " << tcpSocket.bytesAvailable();

        QDataStream stream(&tcpSocket);
        stream.setVersion(QDataStream::Qt_4_5);

        if (nextBlockSize == 0)
        {
            if (tcpSocket.bytesAvailable() < sizeof(quint64)) {
                return;
            }
            stream >> nextBlockSize;
        }
        qDebug () << "nextBlockSize: " << nextBlockSize;
        if (nextBlockSize >tcpSocket.bytesAvailable()) {
            return;
        }

        quint32 ttt;
        stream >> ttt;
       
        QByteArray arrFile;

        stream >> arrFile;
        //qDebug () << "arrFile.size: " << arrFile.size();
        QFile file(inPath);
        file.open(QIODevice::WriteOnly);
        file.write(arrFile);
        qDebug () << "file.size: " << file.size();
        nextBlockSize = 0;
    }
    qDebug () << "discon";
    tcpSocket.disconnectFromHost();
    tcpSocket.waitForDisconnected();
}
//! [4]

Собственно, в чём проблема: в случае с потоками файлы крупнее пары килобайт не передаются. Как выяснилось qDebug'ами - не считываются на сервере. Выдаётся, что tcpSocket.bytesAvailable() значительно меньше nextBlockSize.
Проходит одна итерация цикла forever, тогда как в беспоточном случае их столько, сколько нужно, чтобы tcpSocket.bytesAvailable() стало больше nextBlockSize.

Возникло подозрение, что если в первом случае я связываю сигнал readyRead:
Код:
connect (pSocket, SIGNAL(readyRead()), this, SLOT(pSocketReadyRead()));
то с потоками - нет. Но как связать сигналы правильно - я не понял.

В общем, большая просьба помочь. Кажется, что ошибка не такая уж и большая, но портит всё дело.

Заранее спасибо :-)

P.S. Вышло сумбурно - извините. Если что-то непонятно - постараюсь разъяснить лучше.
P.P.S. Поиском пользовался - не помогло


Название: Re: Передача файлов через QTcpSocket используя QThread
Отправлено: ilyagoo от Март 29, 2011, 10:20
1) run() - не слот
2) создай другой слот и там читай, а в run() оставь создание сокета, connect() и exec()


Название: Re: Передача файлов через QTcpSocket используя QThread
Отправлено: ManOfOrange от Март 29, 2011, 13:41
Охх, сделал - теперь заработало :-) Файл на 15 MB передался без проблем

Спасибо большое :-)