LINUX.ORG.RU

Несколько вопросов по Qt и его QTcpServer

 , , , ,


1

1

1. Дали задание сделать клиент и сервер на Qt, задание сделал и после его отправки, мне ответили, что использование QTcpServer нежелательно, а как делать иначе не знаю (предполагаю, через QTcpSocket), впервые использую Qt для чего либо, поэтому делал по примерам, найденным в сети, а там везде QTcpServer, почему так, если это плохо?

2. Также в задании предполагалась обработка нескольких соединений одновременно, у меня же получается только одно, более того, при подключенном клиенте, если подключится ещё один, начинается какая то вакханалия с передачей данных - второму подключению вместо слова для поиска передаётся часть текста из файла первого, после чего второй переходит в ожидание, пока первый не освободится. В самом начале, когда делал по примеру, предполагал, что такая реализация сервера будет обрабатывать сразу несколько соединений, но оказалось не так, хотел добавить форки, но не стал, подумал это будет не совсем правильно, а правильным будет добавить потоки (QThread), так ли это? Если да, то как это лучше сделать, чтобы не исправлять всё полностью? Если нет, то какой подход лучше использовать?

3. В клиентском clientwindow.cpp в конструкторе я попытался задать регулярные выражения, чтобы word_for_searching не давал вводить недопустимые символы, только буквы и цифры, но, опять-таки, то как это описано в примерах, не сработало, приложение сразу закрывается, хотя всё логично и компилятор не выдаёт предупреждений и ошибок. Что не так?

4. В clientwindow.cpp есть функция loading, её задача показывать анимацию загрузки на экране загрузки из-за неё всё повисает при подключении или анимация вовсе не работает, я подозреваю это тоже нужно делать через потоки?

5. Нужно подсчитать количество слов в файле - когда я передаю файл от клиента серверу есть возможность на сервере сразу вести подсчёт, миную фазу записи в файл, а потом его открытие, считывание и закрытие. Так вот с этим 2 вопроса:

5.1 Если переводить QByteArray в строку, также надо будет париться о том, чтобы очередное слово было полным и уже потом считать, а иначе дочитывать из сокета дополнительные данные, это не будет дольше или займёт такое же количество времени, как если бы я работал с файлом, к тому же из файла можно читать пословно до пробела?

5.2 Как сделать проверку очередного полученного QByteArray на принадлежность к отдельному слову? И как считывая из файла игнорировать спецсимволы? А то, допустим, в тексте «Улыбок тебе, дед макар.» «тебе,» и «макар.» будут интерпретированы, как слова, а если будут пропущены пробелы после точек и запятых, это вообще получится одно большое слово. Я хотел добавить закрытую функцию, которая бы проверяла очередное слово на отсутствие символов точек, запятых и т.д., но вдруг есть более простое решение экономящее время.

https://gitlab.com/iamclock/Qt-question


1. спроси у них

2. нужен код

3. нужен код

4. да

5.1 Если ты передаешь по tcp квантованные данные (не поток) - у тебя должно обеспечиваться разбиение потока на сообщения (где-то в заголовке должна указываться длина). Считываешь сообщение содержащее файл полностью и обрабатываешь как тебе надо.

5.2

    QByteArray buff;
...
    QString text = QString::fromUtf8(buff);
    text.replace('.',' ').replace(',',' ');
    text = text.simplified();
    QList<QString> words = text.split(' ');

anonymous ()
Ответ на: комментарий от anonymous

ссылка внизу на гитлаб с сервером и клиентом, собираются по отдельности, возможно придётся поменять в CMakeLists.txt путь до Qt

clock ()
Ответ на: комментарий от anonymous

«Считываешь сообщение содержащее файл полностью и обрабатываешь как тебе надо.»

Если файл больше 100 Мб или даже, допустим 1 Гб, это будет сильная нагрузка на оперативную память, разве нет?

clock ()
Ответ на: комментарий от clock

допустим 1 Гб, это будет сильная нагрузка на оперативную память, разве нет?

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

anonymous ()

Глянул клиент. Если честно, код плохой. Ну т.е. во время обучения для себя такое писать ещё можно, наверное, ну там поэксперементировать и всё такое. А вот показывать кому-нибудь такое я бы застремался. Можно же хотя бы минимально в порядок привести?

fluorite ★★★★★ ()
	qDebug() << "Is client ready? " << client->isOpen();
	if(*s == 0){
		*s = client->readAll().toInt();
		client->waitForReadyRead(2000);
	}

Как обеспечивается отделение объявляемой длины файла от тела файла? Длина файла передается в виде текста? Шта? У toInt есть ok для проверки валидности подсовываемой строки.

client->waitForReadyRead(2000);
client->waitForReadyRead(1000);

Вот этого в принципе быть не должно, веси на событиях (сигналах).

anonymous ()
Ответ на: комментарий от anonymous

Если же есть требование обработки файла произвольной величины

На этот счёт ничего не было сказано, предполагаю, что файлы могут быть и больше 1 Гб и несколько Кб. Да и вообще делать упор на мощные характеристики сервера не хотел, цель была передавать файл сегментами

clock ()
Ответ на: комментарий от anonymous

веси на событиях (сигналах)

По началу так и было, readyRead вызывался по сигналу, но тогда надо помнить, что какие то данные уже были прочитаны, допустим word постоянно обнуляется, хотел его сделать статиком, а потом подумал это будет не правильно и сделал через wait'ы

clock ()
Ответ на: комментарий от clock

1. Организация исходников.

Ладно, директории headers и sources, содержащие по два файла, ещё терпимо, типа задел на будущее.

Но симлинк на .ui — адов костыль, особенно для такого маленького проекта. Особенно в директории с названием headers.

2. clientwindow.h

Множественное наследование вместо аггрегации — моветон.

На дворе 2017 год, есть nullptr.

Закомментированный деструктор под деструктором. Лень удалить строку?

Невнятные сокращения слотов.

Передача всех параметров по значению (Кутешный COW тебя спасает, но надо бы знать про константные ссылки).

3. clientwindow.cpp

Инклюды с .. — плохо, не понимаешь, как работает твоя система сборки.

Не используешь спиcок инициализации в конструкторе.

Оставлен заккоментированный код. Зачем?

Зачем порт задавать как hex-значение? Выпендриваешься?

Не используется новый синтаксис сигнал-слотов.

Валидатор ставится на виджет (QLineEdit). У тебя хрен знает на что.

Комментарии на русском.

Магические константы.

return для void функции в конце функции??

fluorite ★★★★★ ()
Ответ на: комментарий от clock

Клиент должен быть классом унаследованным от QObject, который создается в newConnection сервера. Кухеши не нужны, тем более метод .value const. Класс «клиент» в конструкторе связывает свой метод вычитывания с readyRead tcp и один раз вызывает этот метод принудительно чтобы вычитать данные которые возможно пришли до считывания. Далее нужен четкий механизм объявления длинны файла. Должен быть определен протокол с понятиями message и header. Если предполагается передача огромных файлов возможно на высокой скорости нужно внести в протокол возможность управления передачей со стороны сервера. Сервер должен уметь сказать «хватит» и «продолжай». Тогда вычитывать tcp, разбирать на слова, остаток оставлять до следующего вызова обработчика readyRead и просить у клиента еще. Клиент не должен посылать за раз более чем Х символов. Сервер должен складывать слова во временный файл. Кстати что дальше со словами делать? Но что-то мне подсказывает что в тестовом задании все это не нужно и можно просто вычитать файл целиков в память.

anonymous ()
Ответ на: комментарий от fluorite

Но симлинк на .ui — адов костыль, особенно для такого маленького проекта. Особенно в директории с названием headers.

Согласен, костыль был сделан, потому что cmake не мог найти этот файл, а как указать cmak'у где искать не знаю, торопился сделать, чтоб работало, сделал так

Множественное наследование вместо аггрегации

это копипаст, возможно это не правильно

Закомментированный деструктор под деструктором. Лень удалить строку?

Возможно пропустил в спешке

Невнятные сокращения слотов

что? всё по названиям сигналов или кнопок

Передача всех параметров по значению

что значит по значению, передаю аргументы в функции, с требуемыми типами или надо приводить к const?

Инклюды с .. — плохо, не понимаешь, как работает твоя система сборки.

cmake делал впервые по примерам и разборам, что понял то сделал, собственно поэтому и

директории headers и sources, содержащие по два файла

так было в обучающем материале, типа лучше сразу сделать по папкам, типа так будет понятно

Оставлен заккоментированный код. Зачем?

чтобы раскомментировать при нужде

Зачем порт задавать как hex-значение? Выпендриваешься?

Просто, какая разница так или 47806

Не используется новый синтаксис сигнал-слотов.
впервые использую Qt для чего либо, поэтому делал по примерам, найденным в сети
Валидатор ставится на виджет (QLineEdit). У тебя хрен знает на что.

LOOOL word_for_searching это и есть QLineEdit вставлял его через редактор

Комментарии на русском.

В чём проблема? Вряд ли это попадёт куда то, где будет другой язык, если только здесь, да и комментариев на русском языке не много

Магические константы.

ОДНА и это порт

return для void функции в конце функции??

пропустил, вероятно изначально что то возвращалось или была заглушкой или нужно было выйти по условию

clock ()

Мы вам перезвоним.

EXL ★★★★★ ()
Ответ на: комментарий от anonymous
Client::Client(QTcpSocket *tcp)
{
    this->tcp = tcp;
    connect(tcp,&QTcpSocket::disconnected,this,&Client::disc,Qt::QueuedConnection);
    connect(tcp,&QTcpSocket::readyRead,this,&Client::readTcp,Qt::QueuedConnection);
    QMetaObject::invokeMethod(this,"readTcp",Qt::QueuedConnection);
}

void Client::readTcp()
{
    if(tcp.isNull()) return;
    rBuff.append(tcp->readAll());
    if(rBuff.size() >= sizeof(quint32))
    {
        msgSize = *(reinterpret_cast<const quint32*>(rBuff.constData()));
        if(rBuff.size() >= msgSize)
        {
            QMetaObject::invokeMethod(this,"parseTcp",Qt::QueuedConnection);
        }
    }
}

void Client::parseTcp()
{
    if(tcp.isNull()) return;
    if(rBuff.size() < sizeof(quint32)) return;
    msgSize = *(reinterpret_cast<const quint32*>(rBuff.constData()));
    rMess = rBuff.mid(0,msgSize);
    rBuff.remove(0,msgSize);
    if(rBuff.size() >= sizeof(quint32))
    {
        msgSize = *(reinterpret_cast<const quint32*>(rBuff.constData()));
        if(rBuff.size() >= msgSize)
        {
            QMetaObject::invokeMethod(this,"parseTcp",Qt::QueuedConnection);
        }
    }

    //do stuff
}

void Server::newConnection()
{
    if(serv.isNull()) return;
    while(serv->hasPendingConnections())
    {
        QTcpSocket* sock = serv->nextPendingConnection();
        if(sock != 0)
        {
            Client* client = new Client(sock);
            connect(client,&Client::disc,this,&VPrmServer::clientDisconnected,Qt::QueuedConnection);
            clients.append(client);
        }
    }
}

void Server::clientDisconnected()
{
    QObject* sender = this->sender();
    if(sender == 0) return;
    clients.removeAll((qobject_cast<Client*>(sender)));
    delete sender;
}
anonymous ()
Ответ на: комментарий от anonymous

Ох, блин, где-нибудь есть разбор чего нить подобного на Qt? Со стороны звучит громоздко

Кстати что дальше со словами делать?

Считать совпавшие

clock ()

код ужасен, особенно waitForBytesWritten
чтото сказать более сложно

x905 ★★★★★ ()

Я бы сделал на том, на чём умеешь хорошо, сославшись на то, что Qt - большой комплексный фреймворк, с которым ты не знаком и которым надо учиться пользоваться отдельно. Предложив например потом посмотреть для сравнения вариант на Qt(после основного решения на знакомой технологии).

Если не умеешь хорошо ни на чём, очевидно ты не подходишь :)

Просто для статистики: на какую должность и на какой уровень зп претендуешь?

pon4ik ★★★★★ ()
Последнее исправление: pon4ik (всего исправлений: 1)
Ответ на: комментарий от pon4ik

Просто для статистики: на какую должность и на какой уровень зп претендуешь?

Пока просто задание, меня могут и не взять из-за начала учебного года

Qt - большой комплексный фреймворк, с которым ты не знаком и которым надо учиться пользоваться отдельно

Ну а с другой стороны, сделав на том, что не известно, можно чему то научиться, я пока делал неделю, мозги свои парил, что то новое узнал, а не балду пинал. Вообще в требованиях были базовые знания Qt, я не предполагал, что будет что то подобное, расчитывал на что то попроще. И как на C++ можно сделать сервер-клиент без буста, Qt и C, как то однажды нужно было такое сотворить, но везде советы были либо через буст, либо с подключением библиотек Си. И кстати, параллельный сервер как всё же лучше делать? через QThread?

clock ()
Ответ на: комментарий от clock

Потоки самый примитивный и топорный способ (хорошо работает на малом количестве подключений).

Если надо в много подключений нужно смотреть в сторону epoll/kqueue/iocp, если надо низкое лэйтенси(насколько это возможно на tcp), то надо смотреть в сторону потоков.

pon4ik ★★★★★ ()
Ответ на: комментарий от clock

Ах, да. Совсем забыл. Есть же ещё неблокирующие сокеты.

pon4ik ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.