Название: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 26, 2011, 19:08 Делаю лабу - чат с использованием tcp-сокетов. Написал 2 программки - клиент и сервер (сервер работает в единственном потоке), все работает, но для лучшего понимания хочу переделать сервер следующим образом: онсновной поток должен только принимать данные (обрабатывать сигналы по-сути) и передавать в отдельный поток, который будет обрабатывать данные и писать в сокет. Проблема в том, что сокеты создаются в главном потоке и сигналы обрабатываются тоже в главном потоке, при создании потока для обработки туда передается указатель на сокет, в теле run() вызывается QTcpSocket.write и вываливается ошибка "QObject: Cannot create children for a parent that is in a different thread.". Не пойму что не так, ведь слоты сокета обрабатываются в том же потоке, откуда идет сигнал (в главном потоке), а в паралельных потоках я всего лишь записываю в сокет...
Я много искал в интернете по поводу того, как адекватно реализовать мою задумку, но ничего толкового так и не нашел. Везде пишут элементарные вещи типа: как ответить одному клиенту (там обычно сокет создается в паралельном потоке и висит при помощи exec(), обрабатывая сигналы), в примере threaded fortune server, который идет вместе с qt вообще тупо создается поток, в теле run создается сокет, отсылает 1 строчку и закрывает сокет, имхо это бред, а не пример. Для тех кто так и не понял в чем вопрос, кратко его можно сформулировать: "как передать в поток список открытых сокетов (или же один сокет), чтобы он записал во все сокеты (или один) данные и уничтожился, не закрывая сокеты?" Вот куски кода (во вложении все) Код: //myserver.cpp - тут отлавливаются сигналы incomingConnection и создается обьекты MyClient Код: //myclient.cpp - тут (главный поток) обрабатываются сигналы Код: //t.cpp - обработка в потоке Название: Re: Запись сокета из разных потоков Отправлено: Странник от Октябрь 26, 2011, 20:12 вызывать методы QTcpSocket можно только из того потока, в котором он живет. еще вопросы?
Название: Re: Запись сокета из разных потоков Отправлено: BRE от Октябрь 26, 2011, 20:17 Подобные темы точно были. Поищи по форуму и найдешь решение.
Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 26, 2011, 22:09 вызывать методы QTcpSocket можно только из того потока, в котором он живет. еще вопросы? Похоже так и есть... Получается палка о двух концах - я не могу делать moveToThread потому, что слоты disconnect(), readyRead() должны срабатывать в главном потоке, не могу создать еще один сокет с таким же дескриптором, и я не могу использовать уже открытый сокет в другом потоке, но почему? Именно это мне и нужно...Название: Re: Запись сокета из разных потоков Отправлено: BRE от Октябрь 26, 2011, 22:23 Лучше начать сначала... Что ты хочешь получить?
Основной поток принимает подключение, создает объект MyClient, дальше этот объект можно переместить в отдельную нить используя moveToThread, где он и будет работать. Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 26, 2011, 22:41 Верно, но тогда сигнал readyRead будет обрабатываться в этой отдельной нити и она должна будет висеть пока не отключится клиент, это плохо и задумка не такая.
Основной поток принимает подключение, создает объект MyClient, когда срабатывает слот MyClient.onReadyRead (в основной нити), в его теле создается отделная нить, которая должна принять указатель на сокет, в который она должна записать данные и уничтожиться. При этом (после создания отдельной нити, передав туда указатель на сокет) основная нить дальше обрабатывает слоты onReadyRead, не задумываясь об ответе клиентам (это перекладывается на отдельные нити). Название: Re: Запись сокета из разных потоков Отправлено: BRE от Октябрь 26, 2011, 22:55 Верно, но тогда сигнал readyRead будет обрабатываться в этой отдельной нити и она должна будет висеть пока не отключится клиент, это плохо и задумка не такая. Операция QTcpSocket::write на самом деле просто копирует данные во внутренний буфер. Сама отправка будет происходить асинхронно, во время работы цикла обработки событий. Дальше данные будут помещаться в буфер отправки tcp-стека и только потом резаться на пакеты и отправляться клиенту. Это я к тому, что нет смысла выполнять запись в сокет в отдельном потоке, эта операция, как правило, выполняется очень быстро.Основной поток принимает подключение, создает объект MyClient, когда срабатывает слот MyClient.onReadyRead (в основной нити), в его теле создается отделная нить, которая должна принять указатель на сокет, в который она должна записать данные и уничтожиться. При этом (после создания отдельной нити, передав туда указатель на сокет) основная нить дальше обрабатывает слоты onReadyRead, не задумываясь об ответе клиентам (это перекладывается на отдельные нити). Можно делать так: одна нить занимается подключением/чтением/записью для всех клиентов, а все долгие операции по подготовке данных для отправки - выполняются в рабочих нитках. Название: Re: Запись сокета из разных потоков Отправлено: Странник от Октябрь 26, 2011, 23:13 либо основной поток занимается подключением клиентов, а чтение, обработка и запись выполняются в отдельном потоке для каждого клиента. хотя в случае с чатом многопоточность не так актуальна - объемы данных как правило невелики, и особой обработки им не требуется.
Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 26, 2011, 23:43 Можно делать так: одна нить занимается подключением/чтением/записью для всех клиентов, а все долгие операции по подготовке данных для отправки - выполняются в рабочих нитках. Видел туториал на ютубе, где такое реализовывали, сначала подумал зачем все так запутанно, но все-равно сделал (после обработки данных в отделной ните, она посылает сигнал основной и оттуда вызывается write(), а отдельная уничтожается), но потом рассказал эту схему преподaвателю, он сказал, что сделал бы так, как описано в первом посте, и я решил попробовать сделать именно так. Дело в том, что он ориентируется на Delphi, а там похоже там совсем другой принцип.Спасибо за ответы. Название: Re: Запись сокета из разных потоков Отправлено: andrew.k от Октябрь 26, 2011, 23:44 2 BRE , ну это все-таки лаба) Поэтому почему бы не быть потокам?
В MyClient передавай не указатель на сокет, а socketDescriptor. А сам сокет создавай в MyClient. И там же все и пиши и читай, зачем основному потоку заниматься записью данных других потоков? Что вообще тогда делают эти потоки? Ниже примерный псевдокод. Код
Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 27, 2011, 00:27 В MyClient передавай не указатель на сокет, а socketDescriptor. Это вариант, но тогда в памяти будет висеть столько потоков (даже не просто висеть, а тратить время процессора?), сколько подключенных клиентов, даже если никто никому ничего не пишет. А сам сокет создавай в MyClient. И там же все и пиши и читай, зачем основному потоку заниматься записью данных других потоков? Что вообще тогда делают эти потоки? В теле идет разбор данных от клиента - если это общее сообщение поток должен разослать его всем активным сокетам, если приватное - то только тем, кому оно послано, если запрос на авторизацию - считать и проверить имя и возможно отправить список имен "кто онлайн".Фишка в том, что если есть активность - создаются потоки, отрабатывают и уничтожаются, а иначе висит лишь один поток, а не много. Название: Re: Запись сокета из разных потоков Отправлено: andrew.k от Октябрь 27, 2011, 01:06 тогда я не понимаю зачем этим потокам вообще указатель на сокет это раз, если они в них не пишут, только данные обрабатывают.
не понимаю зачем потоки для таких элементарных операций это два. они создаваться и удаляться будут дольше чем реально работать. Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 27, 2011, 01:34 Похоже сделать так, чтобы потоки еще и писали данные нельзя (но идея была именно, чтобы писали).
А нужны они просто для понимания (просто я подумал: "А если операции будут не элементарные", и решил переделать с использованием потоков). Название: Re: Запись сокета из разных потоков Отправлено: andrew.k от Октябрь 27, 2011, 02:57 почему нельзя то? смотри ответ #9.
я че зря писал? че ты ничего не понял? в такой реализации пиши себе на здоровье в сокет. Название: Re: Запись сокета из разных потоков Отправлено: Странник от Октябрь 27, 2011, 08:24 не понимаю зачем потоки для таких элементарных операций это два. ну это все-таки лаба) Поэтому почему бы не быть потокам? (C) <= )баловство это все. даже чисто для понимания - не поймешь ничего толком, когда реализация на задаче смотрится примерно как на корове седло. хочешь разобраться - напиши в свободное время какой-нибудь более нагруженный сервер, например, файлы клиентам раздающий. тогда плюсы и минусы каждого решения прочувствуешь на себе. Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 27, 2011, 17:13 почему нельзя то? смотри ответ #9. По-моему я в полной мере ответил в посте #10. Да так можно сделать, но меня интересует возможно ли сделать иначе: сокет создается в основном потоке, сигнал readyRead ловится тоже в основном потоке, в слоте onReadyRead создается поток, который должен записать в сокет, который уже создан, отработать и уничтожиться, не закрывая сокет... почему так сделать нельзя?я че зря писал? че ты ничего не понял? в такой реализации пиши себе на здоровье в сокет. Название: Re: Запись сокета из разных потоков Отправлено: andrew.k от Октябрь 27, 2011, 17:43 можно. почему нельзя?
нельзя из функции run. можно попробовать moveToThread. но придется каждый раз двигать туда-сюда. и наверное не получится) а вообще это то же самое, что удалять гланды через жопу. я понимаю, что это лаба, но все таки. если поток нужен,чтобы обработать данные, пусть их и обрабатывает. писать должен тот, кто создал сокет. Название: Re: Запись сокета из разных потоков Отправлено: andrew.k от Октябрь 27, 2011, 17:44 двигать наверное можно пока сокет не открыт.
Название: Re: Запись сокета из разных потоков Отправлено: dead_vip от Октябрь 28, 2011, 17:43 баловство это все. даже чисто для понимания - не поймешь ничего толком, когда реализация на задаче смотрится примерно как на корове седло. хочешь разобраться - напиши в свободное время какой-нибудь более нагруженный сервер, например, файлы клиентам раздающий. тогда плюсы и минусы каждого решения прочувствуешь на себе. Самый дельный совет оказался =) Оказывается, вызывать методы сокета действительно можно только в том потоке, где он создан, делать иначе не получится. Все мой педантизм... дальше будет лаба ftp-сервер, раздающий файлы.Спасибо всем за ответы. |