Russian Qt Forum
Ноябрь 25, 2024, 08:22 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема в реализации многопоточного сервера. Прошу помощи.  (Прочитано 9195 раз)
AntonUfo
Гость
« : Февраль 17, 2010, 21:44 »

Сабственно вот такая проблема. Имеются клиент и сервер.

На стороне клиента выполняются два действия.
1. По нажатию 1-й кнопки отправляется инфа на сервер с некими данными и начинаются ДОЛГИЕ расчеты
2. По нажатию 2-й кнопки расчеты останавливаются на любом этапе выполнения

На стороне сервера:
После того как клиент приконектился к серверу и нажал на кнопку 1, создается поток в котором создается сокет принимающий инфу от клиента с данными.
Дальше в зависимости от того какие данные приняты type 1 или type 2 или type 3:

type 1 не рассматриваем (быстрый расчет)
type 2 долгий расчет - после создания сокета создается вторичный поток в котором вызываются созданные мной класы и методы
type 3 - после получения запроса с этим типом вторичный поток, сокет, первичный поток останавливаются, клиент отцепляется от сервера.

При закрытии сервера - отцелпяются все присоедененные клиенты
При закрытии клиента на сервер подается команда на закрытия соответствующих потоков и сокета сооедененного с закрывающимся клиентом.

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

Запутался вообще уже блин...



« Последнее редактирование: Февраль 19, 2010, 09:02 от AntonUfo » Записан
AntonUfo
Гость
« Ответ #1 : Февраль 19, 2010, 08:30 »

Все никак разобраться не могу  Непонимающий, весь форум перерыл, никак не получается настроить сигналы и слоты правильно, мне кажется все дело в них, ведь можно же убить вторичный поток через terminate() не дожидаясь пока он закончит свою работу..., не хочу лезть в потоки через volatile bool stop = true, должен быть более правильный вариант..., я весь форум перерыл, на форуме не видел что бы моя проблема обсуждалась, как мне кажется интересно будет многим, да и в литературе ответов нет, се больше простые примерчики....

Хэлп... Подмигивающий

Я подоедактировал исходники сервера, теперь он в  виде последнего варианта который есть у меня..., но косяки блин те же...
« Последнее редактирование: Февраль 19, 2010, 09:04 от AntonUfo » Записан
BRE
Гость
« Ответ #2 : Февраль 19, 2010, 09:05 »

Использовать переменную для выхода из потока лучше (правильный вариант), чем убивать его terminate, т.к. ты сам выбираешь момент завершения потока и может это сделать корректно.
Лучше сначала научиться запускать/останавливать потоки локально, т.е. без использования сети. Так будет проще разобраться. Подмигивающий
Тем с описанием этого метода на форуме несколько.
Не обязательно изменять значение переменной stop явно, лучше сделать в классе своего потока метод (слот) stop, который и будет это делать. По аналогии с QThread::quit, который по сути делает тоже самое, только для цикла обработки событий потока.
Прямой вызов деструктора, например Mthread->~MatchThread(), не освобождает память. Нужно использовать delete Mthread.
Записан
niXman
Гость
« Ответ #3 : Февраль 19, 2010, 09:48 »

Цитировать
не хочу лезть в потоки через volatile bool stop = true, должен быть более правильный вариант...
это и есть самый правильный вариант Подмигивающий

up
глянул коды.
1. переменная "type", одновременно может быть двух значений?
2. соединять сигналы/слоты внутри QThread::run(), не лучшая идея.

совет: не описывайте как у вас это должно работать или как вы хотите чтоб оно работало, а опишите что требуется реализовать. т.е. постановку задачи. это скорее разрешит вашу проблему. т.к. разбираться в чужих исходниках, мало кому охота.
« Последнее редактирование: Февраль 19, 2010, 09:56 от niXman » Записан
AntonUfo
Гость
« Ответ #4 : Февраль 19, 2010, 10:33 »

Цитировать
1. переменная "type", одновременно может быть двух значений?
трех, это признак, клиент отправляет данные на сервер в таком виде:

- размер отправленного пакета
- признак пакета type (1 необходимо выполнить быстрый расчет, 2 медленный и долгий, 3 остановить расчет посланный по соединению ранее соединение не разрывать ждать новых вариантов расчетов)
- исходные данные (их много привел как вариант myListN неважно что это будет число, строка)
 
Цитировать
2. соединять сигналы/слоты внутри QThread::run(), не лучшая идея.
этот сервер я не писал сам полностью, подсмотрел как реализовано в книжке, только в ней нет многопоточности в том виде в котором мне нужно...

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

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

Задача такая.

Реализавать многопоточный сервер и однопоточный клиент. Клиент может посылать три типа запросов:
1. медленные расчеты (на сервер передаются данные для расчета и вызывается на выполнение одна функция)
2. быстрые расчеты (на сервер передаются данные для расчета и вызывается на выполнение другая функция)
3. третий тип запроса останавливает любые расчеты на сервере инициированные I клиентом, при этом соединение клиент сервер не рвется, сервер ждет запрос с задачей от клиента

Ограничений на колличество одновременно подсоединенных клиентов и соответственно ведущихся расчетов нет.
При отключении от сервера I клиента, сервер работает дальше, выполняет запросы и расчеты других подсоедененных клиентов.
При отключении сервера клиентам посылается сообщение что сервер больше не работает

Вот такая мне штука нужна...

Записан
niXman
Гость
« Ответ #5 : Февраль 19, 2010, 11:28 »

Цитировать
трех, это признак
это понятно. но что, одновременно? или поочередно/попеременно?
ну да ладно. попытаемся разобрать задачу по частям...

Цитировать
Реализавать многопоточный сервер и однопоточный клиент. Клиент может посылать три типа запросов:
1. медленные расчеты (на сервер передаются данные для расчета и вызывается на выполнение одна функция)
2. быстрые расчеты (на сервер передаются данные для расчета и вызывается на выполнение другая функция)
3. третий тип запроса останавливает любые расчеты на сервере инициированные I клиентом, при этом соединение клиент сервер не рвется, сервер ждет запрос с задачей от клиента
вот с этого и начнем.
1. в первых двух случаях, помимо самих данных для расчетов, передается еще и тип обработки данных. он может быть двух вариантов.
обобщаем: в обоих случаях, данные передаются одинаково.

я верно понял?
Записан
AntonUfo
Гость
« Ответ #6 : Февраль 19, 2010, 12:03 »

я: трех, это признак
Цитировать
это понятно. но что, одновременно? или поочередно/попеременно?
ну да ладно. попытаемся разобрать задачу по частям...
нажал кнопку сформировать расчет быстрый, пришла инфа с типом 1 начался расчет который может успешно считаться или который можно остановить прислав тип 3
или
нажал кнопку сформировать расчет долгий, пришла инфа с типом 2  начался расчет который может успешно считаться или который можно остановить прислав тип 3

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

Цитировать
1. в первых двух случаях, помимо самих данных для расчетов, передается еще и тип обработки данных. он может быть двух вариантов.
обобщаем: в обоих случаях, данные передаются одинаково.
ну да данные и для быстрых и для медленных расчетов используются одни и те же, после получения данных и типа расчета я их использую в своем классе для вызова нужных мне методов моего класса...

Код:
void MatchThread::run()
{
QString str;
if ( type == 1){
//Таким образом вызываю быстрые расчеты на выполнение
BuildingA *r1 = new BuildingA();
str = (r1->buildingA(myListN));
}
if ( type == 2){
//Таким образом вызываю ДОЛГИЕ расчеты на выполнение
BuildingB *r1 = new BuildingB();
str = (r1->buildingB(myListN));
}
exec();
}

тоесть после того как я получил: соединение - первичный поток - сокет - вторичный поток - во втором потоке запускаю свои расчеты
таким образом если мне необходимо расчеты остановить (второй поток) я могу снова послать данные из клиента сокету на остановку с типом 3


Записан
BRE
Гость
« Ответ #7 : Февраль 19, 2010, 12:38 »

Есть главный поток, в котором живет QTcpServer, ждущий подключения клиента.
Когда подключается новый клиент - сервер создает новый поток (ClientThread), который занимается обслуживанием этого соединения.
При создании ClientThread, он переходит в состояние ожидания команды от клиента.
Команда поступила:
* расчет 1/2 - проверили, выполняется ли расчет и если да - завершили расчте (см. ниже), запустили поток (!) BuildingAThread/BuildingBThread. ClientThread - перешел в состояние ожидания команды от клиента. При завершении расчета, поток расчета отправляет сигнал в ClientThread, который возвращает результат клиенту.
* завершить расчет - проверили, выполняется ли расчет (создан ли объект класса BuildingAThread/BuildingBThread и он в состоянии выполнения). Если выполняется, вызвали его метод stop, дождались когда этот поток реально завершиться (QThread::wait), разрушили объект. ClientThread - перешел в состояние ожидания команды от клиента.

Клиент разорвал связь с сервером: завершили расчет (если он был запущен), запустили позднее разрушение объекта ClientThread (QObject::deleteLater).

Тебе нужно сделать класс потока BuildingAThread/BuildingBThread с возможностью его остановки. Для этого лучше использовать специальный флаг, защищенный мьютексом. Этот флаг необходимо проверять в цикле расчета и когда он установлен (или сброшен) выходить из метода run.
На форуме есть несколько тем, где это показано.

Ну а если будут трудности, напиши. Будем постить куски кода.  Подмигивающий
Записан
niXman
Гость
« Ответ #8 : Февраль 19, 2010, 13:05 »

в main()
Код
C++ (Qt)
QTcpServer server;
...
class type1_processor: public QThread {
  type1_processor(QTcpSocket* s):sock(s),stop(false) {
     читаем все данные из сокета....
     после того как прочли все данные...
     соединяем сигнал сокета readyRead() со слотом type3_writen()
  }
private slots:
  void type3_writen() {
     if ( is_type3(sock) ) {
        stop = true;
     }
  }
  /*  */
  void run() {
     while ( !stop ) {
        тут мы работаем с данными.
     }
  }
  /*  */
  QTcpSocket* sock;
  volatile bool stop;
};
 
class type2_processor: public QThread {
  по аналогии...
};
 
int main() {
  while ( server.waitForNewConnection() ) {
     QTcpSocket* sock = server.nextPendingConnection();
     if ( is_type1(sock) ) {
        type1_processor(sock);
     } else if ( is_type2(sock) ) {
        type2_processor(sock);
     }
  }
}
 

использовать как псевдокод.
возможно чего-то не учел...
« Последнее редактирование: Февраль 19, 2010, 13:16 от niXman » Записан
niXman
Гость
« Ответ #9 : Февраль 19, 2010, 13:28 »

а у серверной программы есть ГУЙ?
просто используя boost.asio, это очень просто решается.
Записан
AntonUfo
Гость
« Ответ #10 : Февраль 19, 2010, 13:40 »

Цитировать
а у серверной программы есть ГУЙ?
просто используя boost.asio, это очень просто решается.
спасибо за псевдокод !!! буду вдумывать  Смеющийся

пока нет, но скорее всего будет..., аппетит приходит во время еды..
а что такое boost.asio ?

Записан
niXman
Гость
« Ответ #11 : Февраль 19, 2010, 14:04 »

Цитировать
а что такое boost.asio ?
http://www.boost.org/doc/libs/1_42_0/doc/html/boost_asio.html

Цитировать
пока нет, но скорее всего будет...
никогда не видел серверов с гуем Смеющийся
хотя от многих слышу что такие есть! Подмигивающий
Записан
AntonUfo
Гость
« Ответ #12 : Февраль 19, 2010, 15:37 »

Огромное спасибо за помощь, в общем то все получилось, сделал остановку вторичного потока через специальный флаг. (т.е. если флаг true заканчиваю выполнять методы расчета и выхожу из QThread::run()Подмигивающий

Вот пример из книги Юрия Земскова с которого собственно все и началось, только вот такой нюансик как победить ?

После того как клиент приконектился к серверу и создался поток - сокет, я закрываю клиента (жму на крестик), но из списка выполняемых потоков me_threads он не удаляется, т.е. не работает метод void EchoServer::removeThread()

Код:
void EchoServer::removeThread(){
    EchoThread *thread = qobject_cast<EchoThread*>(sender());
    m_threads.removeAt(m_threads.indexOf(thread));
    thread->deleteLater();
}

Вот что он про него пишет:
"При получении сигнала о завершении потока выясняем, какой именно поток сгенерировал этот сигнал, ставим этот поток в очередь на уничтожение и удаляем из списка потоков"

обьясните плз, что он делает "на пальцах", и почему не работает, а то получается что соединения нет, а в списке поток остается... хотя память вроде освобождает...

Вот пример из книги полностью.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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