Переместите QTcpSocket в новый поток после установления соединения

У меня есть потоковый сервер.

QTcpSocket необходимо создать в потоке, в котором он должен быть запущен, FI: Qt - Обрабатывать QTcpSocket в новом потоке, передавая дескриптор сокета.

Моя проблема в том, что мне нужно иметь пул потоков и перемещать сокет в определенный поток ПОСЛЕ того, как клиент отправил определенный токен, который определяет, в каком потоке должен быть сокет.

Другими словами, мне нужно прочитать сокет, чтобы заранее знать, в какой поток его поместить.

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

Примечание. Невозможно инициализировать два абстрактных сокета с одним и тем же родным дескриптором сокета.

Другим решением является создание сокета в отдельном потоке, а затем объединение обоих потоков вместе, хотя я не знаю, возможно ли это.

Или, возможно, иметь возможность прочитать дескриптор сокета в основном потоке перед вызовом setSocketDescriptor в дочернем потоке, если это вообще возможно?


person Damien    schedule 06.07.2016    source источник
comment
из любопытства, почему вы должны заставить клиента сказать вам, в каком потоке вы должны запустить свой QTcpSocket?   -  person Mike    schedule 06.07.2016
comment
конкретный токен, который определяет, в каком потоке должен быть сокет. Интересно, как вы можете указать, в каком потоке должен быть сокет. Насколько я знаю, вы не можете указать идентификатор потока при создании потока. Если вы хотите указать имя потока, вы можете просто вызвать thread-->setObjectName(Name), и это не имеет ничего общего с сокетами.   -  person rightaway717    schedule 06.07.2016
comment
почему бы вам просто не сделать socket-›moveToThread(otherthread)?   -  person λuser    schedule 06.07.2016
comment
Док говорит, что это запрещено   -  person Damien    schedule 07.07.2016
comment
Вы можете смотреть на это как на сервер кластера пользователей. Пользователь может создать кластер или присоединиться к существующему. Каждый кластер имеет свой собственный поток для повышения производительности и использования многоядерности.   -  person Damien    schedule 07.07.2016


Ответы (1)


Вы можете абсолютно легко перемещать сокеты по QThreads, просто обратите внимание на четыре вещи:

1) Перед перемещением убедитесь, что у вашего QTcpSocket нет родителя

2) Отключите все от объекта сокета перед перемещением

3) Подключите все, что вам нужно, обратно в функцию, которая выполняется в потоке назначения (вы можете использовать какой-то пул в потоке, где эти «перемещенные» объекты хранятся до того, как поток заберет их

4) Вызовите readAll() после инициализации, так как вы можете пропустить некоторые сигналы readyRead()

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

person evilruff    schedule 06.07.2016
comment
Вы получите предупреждения от QIODevice во время записи данных. Потому что не все поля QTcpSocket можно перемещать между потоками. - person Dmitry Sazonov; 07.07.2016
comment
В последние несколько лет все работает нормально, так как я использовал эту технику во многих проектах. Можете ли вы уточнить, какое предупреждение вы имеете в виду? - person evilruff; 07.07.2016
comment
Я постараюсь найти больше, потому что у меня нет доступа к кодовой базе. Но было следующее предупреждение: QSocketNotifier: уведомители сокетов не могут быть включены из другого потока. - person Dmitry Sazonov; 07.07.2016
comment
Честно говоря, я никогда не видел ничего подобного .. Я посмотрю исходники QT утром, так как уже немного поздно ) - person evilruff; 07.07.2016
comment
Просто чтобы уточнить.. если вы перемещаете сокет в поток, весь доступ к сокету должен происходить в этом новом объявлении.. вы не можете перемещать сокет куда-то и писать из основного потока.. это может привести к предупреждению, которое вы упомянули. - person evilruff; 07.07.2016
comment
Никаких отключений делать не надо - это просто глупо. Остальные пункты действительны. QTcpSocket определенно можно перемещать между потоками, по крайней мере, в OS X и Windows, я делаю это все время, и это просто работает. У сокета может быть родитель, затем вам нужно удалить его (например, связать его с переходным объектом) перед переключением потоков. - person Kuba hasn't forgotten Monica; 07.07.2016
comment
Куба, отключение необходимо в случае, если сокет использовался в другом потоке, скажем, у вас есть основной поток, который инициирует соединения, обрабатывает ошибки, подключенные и т. Д. Сигналы, поэтому тогда соединение установлено, и вы хотите переместить сокет на рабочий иногда это имеет смысл. , все зависит от того, как сокет использовался до перехода к потоку. - person evilruff; 07.07.2016
comment
Это не то, что вам нужно делать, например, вы передаете сокет из nextPendingConnection, но есть варианты использования, тогда это чище - person evilruff; 07.07.2016
comment
Все, что имеет значение, это то, что вы получаете доступ к сокету из его thread(). В идеале объект контроллера, который управляет сокетом, должен стать родителем сокета, и вы переместите контроллер в другой поток, а за ним следует и сокет, и все прекрасно, поскольку соединения прямые, но будут вызываться из цикла событий правильного потока. Это действительно не должно быть сложно, когда вы делаете это правильно :) - person Kuba hasn't forgotten Monica; 07.07.2016
comment
Куба, я согласен с тобой на 101%, и делаю это во многих очень многих проектах, и именно это я пытаюсь сказать в своем ответе =) - person evilruff; 07.07.2016
comment
@KubaOber, я заметил это в примере Threaded Fortune Server они переопределяют incomingConnection и передают собственный дескриптор сокета в новый поток. Если QTcpSocket можно безопасно переместить в другой поток, нельзя ли это реализовать, просто получив QTcpSocket из nextPendingConnection и переместив его в новый поток? - person Mike; 07.07.2016
comment
Обратите внимание на их утверждение: мы создаем этот объект внутри потока, который автоматически связывает сокет с циклом событий потока. Это гарантирует, что Qt не будет пытаться доставить события в наш сокет из основного потока, пока мы обращаемся к нему из FortuneThread::run().. - person Mike; 07.07.2016
comment
На самом деле это не имеет значения, пример Fortune Server, прежде всего, очень-очень старый, и в Qt целую вечность абсолютно нормально передавать QTcpSocket - person evilruff; 07.07.2016
comment
Итак, может ли кто-нибудь подтвердить, что пример Threaded Fortune Server теперь можно безопасно реализовать так, как я описал? - person Mike; 07.07.2016
comment
Почему бы вам просто не взять код, изменить его и попробовать? знак равно - person evilruff; 07.07.2016
comment
К сожалению, многие примеры Qt серьезно устарели и являются скорее демонстрацией того, что Qt стремится сохранить работоспособность старого кода, чем того, как вы должны реализовывать новые функциональные возможности. Поскольку Qt 5.7 требует C++11, каждый пример должен быть переписан в современном стиле C++ и т. д. - person Kuba hasn't forgotten Monica; 07.07.2016
comment
@evilruff, я могу взять код, но если он не падает и не выдает предупреждений, это НЕ означает, что он все время безопасен. . . - person Mike; 07.07.2016
comment
Что ж, тогда вы должны это проверить =) и если вы застряли, мы будем рады помочь.. но все начинается с рук =) - person evilruff; 07.07.2016
comment
Кроме того, в документе четко указано, что это не разрешено, даже если это работает с образцом кода, это не означает, что это будет работать в будущей версии Qt или может иметь какое-то неопределенное поведение. Возможно, это будет билет или запрос функции для Qt? - person Damien; 19.07.2016
comment
в документе QTcpServer, два утверждения. Примечание. Если вы хотите обрабатывать входящее соединение как новый объект QTcpSocket в другом потоке, вы должны передать socketDescriptor в другой поток и создать там объект QTcpSocket и использовать его метод setSocketDescriptor(). и Возвращенный объект QTcpSocket нельзя использовать из другого потока. Если вы хотите использовать входящее соединение из другого потока, вам необходимо переопределить incomingConnection(). @evilruff - person Damien; 10.03.2017
comment
Ну, я знаю, что там написано, но опять же, я не изменил своего мнения, я знаю, что это работает, и, глядя в исходники Qt, я не вижу ни одной причины, почему это не должно.. - person evilruff; 10.03.2017