Russian Qt Forum

Qt => Работа с сетью => Тема начата: qtkoder777 от Апрель 20, 2016, 17:53



Название: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 20, 2016, 17:53
Здравствуйте.
Возникла задача написать такой сервер:
Сервер должен получить данные от клиентов, провести над ними некоторые вычисления и вернуть результат клиенту.
В процессе вычислений могут поступать новые запросы.

Пытаюсь сделать так:
Ввёл структуру, в которой запоминаю сокет клиента, которому надо отослать результат работы с data_.
Код
C++ (Qt)
struct ThreadData
{
     Data data_;
     QTcpSocket* socket_;
};
и очередь QQueue этих структур, которая заполняется в основном потоке

Вычисления крутятся в отдельном потоке наследнике QThread.
Данные извлекаются из очереди, обрабатываются, и отправляются клиенту с помощью
Код
C++ (Qt)
socket_->write()
, если очередь пуста, крутится бесконечный цикл.

Фактически, из потока вычислений данные НЕ ДОХОДЯТ до клиента, хотя write завершается без ошибки.
В основном потоке данные по тому же сокету отправляются и доходят.

Читал где то на другом форуме, что сокеты нельзя передавать между потоками.
Так ли это? И как надо поступать?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 21, 2016, 07:47
В рабочих нитках можно только обрабатывать, а результат возвращать в нить сетевого обмена, из которой и отправлять ее клиенту.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 21, 2016, 11:18
Откуда сокет вообще знает в какой он нитке - это особенность Qt?
Подходит ли вообще Qt для написания таких программ?
Может стоит смотреть в сторону других библиотек, раз в Qt такие сложности.


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 21, 2016, 11:24
Откуда сокет вообще знает в какой он нитке - это особенность Qt?
Для того, что бы обеспечить правильную работу сигналов в многопоточных программах, в Qt введено понятие контекст потока.
Изменяется он с помощью метода: QObject::moveToThread.

Подходит ли вообще Qt для написания таких программ?
Может стоит смотреть в сторону других библиотек, раз в Qt такие сложности.
Скажем так, такие программы на Qt можно писать. Насколько это будет эффективно вопрос другой.
Я лично использую для этого boost::asio.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 16:37
В рабочих нитках можно только обрабатывать, а результат возвращать в нить сетевого обмена, из которой и отправлять ее клиенту.
А как вернуть результат в нить сетевого обмена?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 16:59
А как вернуть результат в нить сетевого обмена?
Например, можно возвращать готовый для отбравки объект QByteArray.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 17:32
А как вернуть результат в нить сетевого обмена?
Например, можно возвращать готовый для отбравки объект QByteArray.
Как сообщить что данные готовы к отправке? Сигнал-слот?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 17:58
Сигнал-слот?
Как вариант. :)


Название: Re: Передача сокета в другой поток
Отправлено: sergek от Апрель 23, 2016, 18:00
Сигнал-слот?
Как вариант. :)
А это не единственно правильный вариант? :)


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 18:08
А это не единственно правильный вариант? :)
Конечно нет. Для передачи результатов, так же как и для передачи заданий, можно использовать самописные очереди, мэилбоксы и другие механизмы межпоточных взаимодействий.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 18:41
Как следует поступать когда потоку "нечего делать"?
Крутиться в бесконечном цикле и проверять, не появилось ли чего в очереди - это правильно или нет?



Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 18:46
В идеале он должен спать и просыпаться только когда появилась работа. Поищите по форуму, было несколько тем с примерами про очереди и QWaitCondition.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 19:02
Какие параметры должен иметь сигнал для передачи результатов?
Сигнал получат все потоки соединений, надо как-то узнать какому потоку предназначен сигнал.
То есть два параметра - строка результата и this потока?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 19:16
В самом простом случае с Qt-сигналами: сетевая нитка должна передать рабочей какой-то идентификатор клиента/сокета, от которого получен этот запрос и исходные данные для работы. После завершения обработки рабочая нитка должна передать сетевой тот же идентификатор клиента/сокета и результат работы. Сетевая нитка по идентификатору находит нужный сокет и отправляет в результат.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 19:17
Какие особенности у межпоточного connect-a?
Обнаружилось, что после такого connect-a сокет, который в нити thread, перестаёт получать данные (ft - нить вычислений)
Код
C++ (Qt)
connect(&ft, SIGNAL(dataReady(const QString &)), thread, SLOT(slotSendData(const QString &)));
Код
C++ (Qt)
void FortuneThread::slotSendData(const QString &s)
{
tcpSocket_->write(s.toAscii());
}
Есть какой-то загадочный последний необязательный параметр, может там чего надо прописать?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 19:23
Рекомендую вам сначало разобраться с работой сигналов между нитками, а только потом добавлять туда сеть. На форуме много это обсуждалось, есть масса статей интернете. Там есть несколько тонкостей.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 19:33
Рекомендую вам сначало разобраться с работой сигналов между нитками, а только потом добавлять туда сеть. На форуме много это обсуждалось, есть масса статей интернете. Там есть несколько тонкостей.
А можете дать ссылку на какую нибудь хорошую статью?
Статей то много, но непонятно чему верить а чему нет. Нет времени всё перепробовать.
Что-то такое пишут - это правильный способ?
Код
C++ (Qt)
class Worker : public QObject
{
Q_OBJECT
 
public slots:
void doWork() {
/''' … */
}
};
 
/'
''*/
QThread thread;
Worker worker;
connect(obj, SIGNAL (workReady()), &worker, SLOT (doWork()));
worker.moveToThread(&thread);
thread.start();
Может есть в сети готовые исходники сервера с обработкой заданий?

А на boost это легче написать?
С boost совсем не знаком.


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 20:09
А можете дать ссылку на какую нибудь хорошую статью?
К сожалению ссылок не знаю. Основные моменты там в том, что если инициатор сигнала расположен в другом потоке от получателя, то слот вызывается не в момент испускания сигнала, а через очередь сообщений потока-получателя. Т.е. для работы этого механизма, в потоке-получателе должен крутиться цикл обработки событий. Все аргументы сигнала сохраняются в событии и слот вызывается с копиями этих аргументов. Последний параметр connect как раз отвечает за тип подключения.

Что-то такое пишут - это правильный способ?
Это рабочий способ.

А на boost это легче написать?
С boost совсем не знаком.
Легче, если умеете на нем. Если не знакомы, то с Qt будет проще.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 20:46
Я поставил последний параметр Qt::QueuedConnection и это не помогло.
Написано что по умолчанию Qt сам определяет один поток или разные.
Почему сокет не принимает данные если сделать connect?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 20:56
Я поставил последний параметр Qt::QueuedConnection и это не помогло.
Написано что по умолчанию Qt сам определяет один поток или разные.
Сокет не принимает данные.
Метод научного тыка здесь вряд-ли поможет. Вначале разберитесь просто с сигналами, что бы один поток посылал сигнал, а во втором срабатывал слот. Погоняйте туда-сюда данные.
А уже потом добавляйте сеть. Я уже писал, но повторю: для работы с клиентами достаточно одного потока, в котором и будут работать все сокеты, а вот для обработки можно использовать пул рабочих потоков.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 21:02

Метод научного тыка здесь вряд-ли поможет. Вначале разберитесь просто с сигналами, что бы один поток посылал сигнал, а во втором срабатывал слот.
По каким материалам разбираться? Вот вы что читали?
Где-то пишут что надо использовать moveToThread. А без этого можно?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 21:09
По каким материалам разбираться? Вот вы что читали?
Документацию + смотрел исходники Qt. Но я Qt начал использовать году в 2000, так что все эти "нововведения" с межпоточными сигналами появлялись на моих глазах. :)

Где-то пишут что надо использовать moveToThread. А без этого можно?
С помощью этого метода вы указываете в контексте какой нитки будет существовать объект. Это важно для правильного определения типа коннекта: если объект инициатор и объект получатель в разных потоках, то будет использоваться вызов слота получателя через его очередь сообщений.


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 21:21
Нужна волшебная строчка, которая законнектит сигнал из одного потока на слот в другом потоке.
Почему нельзя её написать?
Или это страница кода, а не строчка?
Потоки делаю как наследники от QThread согласно документации на Qt. Хотя кое-где пишут что это неправильно.
Вот эта статья более не менее рабочая?
https://habrahabr.ru/post/150274/


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 23, 2016, 21:28
Нужна волшебная строчка, которая законнектит сигнал из одного потока на слот в другом потоке.
Почему нельзя её написать?
Или это страница кода, а не строчка?
Потоки делаю как наследники от QThread согласно документации на Qt. Хотя кое-где пишут что это неправильно.
Нет никаких волшебных строчек. Для демонстрации нужно написать два класса, я сейчас пишу с телефона, у меня нет возможности набирать код.

Я бы не затевался с QThread, я бы взял QtConcurrent для выполнения функций в отдельных потоках + QFutureWatcher для определения завершения расчета и получения результата. Посмотрите на QtConcurrent, это высокоуровневая надстройка над пулом потоков.

QFuture<T> QtConcurrent::run(Function function, ... )


Название: Re: Передача сокета в другой поток
Отправлено: qtkoder777 от Апрель 23, 2016, 22:49
А сервер не обязан быть многопоточным - на каждого клиента свой поток?


Название: Re: Передача сокета в другой поток
Отправлено: Old от Апрель 24, 2016, 06:26
А сервер не обязан быть многопоточным - на каждого клиента свой поток?
Конечно не обязан, более того такой сервер не жизнеспособен, когда должен обслуживать тысячи клиентов одновременно.


Название: Re: Передача сокета в другой поток
Отправлено: Igors от Апрель 24, 2016, 07:52
Что-то такое пишут - это правильный способ?
Да. Хотя наследование от QThread будет работать с тем же успехом - создание worker'а более идейно/концептуально, в общем "рекомендуют".

Почему сокет не принимает данные если сделать connect?
Сокет-сокет.. Вы сначала убедитесь что сконнектились верно, т.е. сигнал испущенный из одной нитки принимается в другой. Для этого надо сделать moveToThread как в примере (или moveToThread(this) если наследуетесь от QThread). Для проверки поставьте печать (напр qDebug() << QThread::currentThread()) тут и там.