Russian Qt Forum
Июнь 04, 2020, 14:07 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Оцените код передачи файла через QTcpSocket  (Прочитано 413 раз)
vebmaster
Новичок

Offline Offline

Сообщений: 46


Просмотр профиля
« : Май 17, 2020, 22:10 »

Здравствуйте.
Потребовалось сделать передачу файла через QTcpSocket. Нагуглил много разных вариантов, которые меня запутали.
Первым способом сделал через передачу длины блока и затем сам блок. Но потом нашёл более продвинутый способ через транзакции, который появился в Qt 5.7. На нём и сделал, асинхронно.
Оцените пожалуйста, правильно ли и на сколько грамотно сделано? Если есть какие то замечания, буду рад узнать их. Спасибо.

Передаю в несколько переменных:
- тип сетевого пакета (файл, сообщение или другое)
Если файл, то:
- имя файла
- размер файла в байтах
- блок данных
- и в конце текстовое сообщение

Код передачи файла:
Код:
enum PacketType
{
    TYPE_NONE = 0,
    TYPE_MSG = 1,
    TYPE_FILE = 2,
};

void TcpClient::socketSendMessage()
{
    QDataStream stream(m_pTcpSocket);
    stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);

    stream << PacketType::TYPE_FILE;

    QString fileName("/mnt/d/1.png");
    QFile file(fileName);
    QFileInfo fileInfo(file);
    qint64 fileSize = fileInfo.size();

    stream << fileName;
    stream << fileSize;

    int countSend = 0;

    if (file.open(QFile::ReadOnly))
    {
        while(!file.atEnd())
        {
            QByteArray data = file.read(32768*8);
            stream << data;
            countSend++;
        }
        qDebug() << Tools::getTime() << "_CLIENT: ------------------------ countSend FINAL: " << countSend;
    }

    file.close();

    qDebug() << Tools::getTime() << "_CLIENT: send file ok";

    QString testStr("TEST_MESSAGE");
    stream << testStr;
}

Код получения файла:
Заголовочный сервера:
Код:
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H

#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include "global.h"
#include <QFile>


class MyTcpServer : public QObject
{
    Q_OBJECT

public:
    explicit MyTcpServer(QObject *parent = nullptr);
    ~MyTcpServer();

    int number;
    QString str;

public slots:
    void slotNewConnection();
    void slotServerRead();
    void slotClientDisconnected();
    void onSocketReceiveMessage();
    void startServer();

private:
    QTcpServer * mTcpServer;
    QTcpSocket * mTcpSocket;
    qint64 sizeReceivedData;
    QString fileCopy;
    PacketType packetType;

    QString filePath;
    qint64 fileSize;
    QString testStr;
    QByteArray tmpBlock;
    int countSend;

    bool receiveFile(QDataStream &stream);
};

#endif // MYTCPSERVER_H
В конструкторе:
Код:
   packetType = PacketType::TYPE_NONE;
    filePath.clear();
    fileSize = 0;
    testStr.clear();
    sizeReceivedData = 0;
    tmpBlock.clear();
    countSend = 0;
Слот получения сообщения:
Код:
void MyTcpServer::onSocketReceiveMessage()
{
if (!mTcpSocket || !mTcpSocket->bytesAvailable())
return;

qDebug() << Tools::getTime() << "SERVER: --------------------new-----------------------";
qDebug() << Tools::getTime() << "SERVER: onSocketReceiveMessage: bytesAvailable" << mTcpSocket->bytesAvailable();

QDataStream stream(mTcpSocket);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);

// Считывание PacketType
if (packetType == PacketType::TYPE_NONE) {
stream.startTransaction();
stream >> packetType;
if (!stream.commitTransaction()) {
qDebug() << Tools::getTime() << "SERVER: packetType - FAIL commitTransaction";
return;
}
qDebug() << Tools::getTime() << "SERVER: type:" << packetType;
}

if (packetType == PacketType::TYPE_MSG)
{
//
}
else if (packetType == PacketType::TYPE_FILE)
{
//====================================================
// Получение filePath

if (filePath.isEmpty()) {
stream.startTransaction();
stream >> filePath;
if (!stream.commitTransaction()) {
qDebug() << Tools::getTime() << "SERVER: filePath - FAIL commitTransaction";
return;
}
qDebug() << Tools::getTime() << "SERVER filePath:" << filePath;
}

//====================================================
// Получение fileSize

if (!fileSize) {
stream.startTransaction();
stream >> fileSize;
if (!stream.commitTransaction()) {
qDebug() << Tools::getTime() << "SERVER: fileSize - FAIL commitTransaction";
return;
}
qDebug() << Tools::getTime() << "SERVER: fileSize:" << fileSize;
}

//====================================================
// Получение файла

if (sizeReceivedData != fileSize)
{
filePath = this->fileCopy; // временная замена имени файла
QFile file(filePath);
file.open(QFile::Append);

// Работа с файлом в цикле "пока в сокете есть данные"
while (!mTcpSocket->atEnd())
{
//====================================================
// Получение tmpBlock

stream.startTransaction();
stream >> tmpBlock;

if (!stream.commitTransaction()) {
qDebug() << Tools::getTime() << "SERVER: tmpBlock - FAIL commitTransaction";
break;
}

qint64 toFile = file.write(tmpBlock);

sizeReceivedData += toFile;
countSend++;

tmpBlock.clear();

if (sizeReceivedData == fileSize)
break;

} // while (!mTcpSocket->atEnd())

file.close();

} // if (sizeReceivedData != fileSize)

if (sizeReceivedData != fileSize)
return;

qDebug() << Tools::getTime() << "SERVER: sizeReceivedData END: " << sizeReceivedData;
qDebug() << Tools::getTime() << "SERVER fileSize ORIG:" << fileSize;
qDebug() << "SERVER: countSend FINAL: " << countSend;


//====================================================
// Получение testStr

if (testStr.isEmpty()) {
stream.startTransaction();
stream >> testStr;
if (!stream.commitTransaction()) {
qDebug() << Tools::getTime() << "SERVER: testStr - FAIL commitTransaction";
return;
}
qDebug() << Tools::getTime() << "SERVER: testStr:" << testStr;
}

qDebug() << Tools::getTime() << "SERVER: END - bytesAvailable:" << mTcpSocket->bytesAvailable();

// Очистка переменных
filePath.clear();
fileSize = 0;
tmpBlock.clear();
sizeReceivedData = 0;
testStr.clear();
countSend = 0;

} // else if (packetType == PacketType::TYPE_FILE)
}
« Последнее редактирование: Май 18, 2020, 13:44 от vebmaster » Записан
qate
Супер
******
Offline Offline

Сообщений: 1055


Просмотр профиля
« Ответ #1 : Май 18, 2020, 23:33 »

рабочий пример (проект на gihub) былобы удобнее оценивать
Записан
vebmaster
Новичок

Offline Offline

Сообщений: 46


Просмотр профиля
« Ответ #2 : Май 25, 2020, 19:23 »

рабочий пример (проект на gihub) былобы удобнее оценивать
Добавил на https://github.com/vebmaster/QtFileTransferViaSocket
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 805


Погроммист


Просмотр профиля
« Ответ #3 : Май 29, 2020, 18:46 »

Ну сам код конечно ужасно спроектирован, но на первый взгляд должен отправлять файл...

1) Название класса некорректное, более правильное FileSender
2) У твоей синхронной функции почему-то закомментирован waitForBytesWritten, это как раз для блокирующей отправки должно быть.
3) Почему параметрически не передается имя файла и адрес сервера?
4) Повторный вызов метода отправки надо блокировать (кидать ошибку)
5) Нет нормальной обработки ошибок
6) Не все переменные инициализированы в конструкторе, это приведет к крэшам

И в целом кривой подход. Я бы переиспользовал одно TCP соединение, поэтому надо вынести создание и коннект QTcpClient из этого класса, отправлять файл пакетами, реализовать очередь пакетов на отправку, дополнить отправляемые пакеты размером, чтобы можно было отделить данные одного пакета от другого.
« Последнее редактирование: Май 29, 2020, 19:10 от navrocky » Записан

Гугль в помощь
vebmaster
Новичок

Offline Offline

Сообщений: 46


Просмотр профиля
« Ответ #4 : Май 29, 2020, 18:53 »

Ну сам код конечно ужасно спроектирован
Спасибо, можете добавить пару замечаний по поводу того, что именно не так.
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 805


Погроммист


Просмотр профиля
« Ответ #5 : Май 29, 2020, 19:09 »

Ответил развернуто выше
Записан

Гугль в помощь
vebmaster
Новичок

Offline Offline

Сообщений: 46


Просмотр профиля
« Ответ #6 : Май 29, 2020, 19:27 »

2) У твоей синхронной функции почему-то закомментирован waitForBytesWritten, это как раз для блокирующей отправки должно быть.
Закоментил, потому что и так отправляет хорошо. Как говорят, если всё работает хорошо - ничего не трогай.

3) Почему параметрически не передается имя файла и адрес сервера?
Черновой вариант.

4) Повторный вызов метода отправки надо блокировать (кидать ошибку)
Не понял. Где именно кидать ошибку и зачем.

6) Не все переменные инициализированы в конструкторе, это приведет к крэшам
Если посмотреть main.cpp, то можно заметить, что я каждый экземпляр ввожу в отдельный поток.
А если указатель класса инициализировать в конструкторе, то объект создастся не в новом потоке, а в основном (из main.cpp)

Я бы переиспользовал одно TCP соединение
Так в клиенте и сервере везде создаётся по одному сокету и в него пишется через QDataStream.

отправлять файл пакетами, реализовать очередь пакетов на отправку, дополнить отправляемые пакеты размером, чтобы можно было отделить данные одного пакета от другого
Можете показать пример, не совсем понимаю как это пакетами.
Пакеты шлёт уже сам TCP/IP. Это уже другой уровень. Или вы про что то другое?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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