Russian Qt Forum

Qt => Работа с сетью => Тема начата: Ground от Октябрь 09, 2012, 12:16



Название: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 12:16
Доброго времени суток!
У меня имеется обычный такой сервер, полностью писанный на Qt. В последнее время, количество запросов к нему увеличилось, и сервер начал падать.
Прежде всего, стоит пару слов сказать о том, в каких случаях падает сервер. Случаются моменты, когда поток запросов на сервер довольно значительный. Клиент подключается, что-то передает (порядка 100Б), отключается. В коде для подключенного клиента выделяется сокет. Как только клиент отключается - мы по сигналу disconnected() от этого сокета выполняем отключение, в т.ч. и удаляем память. В коде это выглядит так:

При подключении клиента:
Код
C++ (Qt)
connect(mClients[clientNumber].data(), SIGNAL(disconnected()),
           this, SLOT(slotRejectClient()));

При вызове слота:
Код
C++ (Qt)
// Отключение клиента
int Server::slotRejectClient()
{
   int clientNumber = -1;
   // Поиск клиента, которого требуется отключить
   for (int i = 0; i < mMaxClients; i++)
   {
       if (mClients[i].data() != qobject_cast<QTcpSocket*>(sender()))
           continue;
       clientNumber = i;
       break;
   }
 
   if (clientNumber < 0)
       return -1;
 
   return this->disconnectClient(clientNumber);
}

Ну и наконец чистка памяти:
Код
C++ (Qt)
// Отключение и удаление клиента
int Server::disconnectClient(quint16 clientNumber)
{
   disconnect(mClients[clientNumber].data(), SIGNAL(readyRead()),
              this, SLOT(slotReceiveData()));
   disconnect(mClients[clientNumber].data(), SIGNAL(disconnected()),
              this, SLOT(slotRejectClient()));
   disconnect(mClients[clientNumber].data(), SIGNAL(error(QAbstractSocket::SocketError)),
              this, SLOT(slotClientSocketError(QAbstractSocket::SocketError)));
 
   if (!mClients[clientNumber].isNull())
   {
       // Сокет
       mClients[clientNumber].data()->close();
       mClients[clientNumber].data()->deleteLater();
       mClients[clientNumber] = 0;
   }
 
   emit signalSendTextToLog(QString("Клиент №%1 отключен!").arg(clientNumber));
 
   return 0;
}

Так вот, проблема в том, что в функции disconnectClient() при вызове mClients[clientNumber].data()->deleteLater(); сервер падает с сегфолтом. По стеку - вообще ничего не видно, одни системные вызовы. Если убрать строку - сервер работает отлично, правда утекает память.

Стек вызовов на момент падения:
Код
C++ (Qt)
0 QAbstractSocketPrivate::canReadNotification qabstractsocket.cpp 672 0x6cbc0172
1 QAbstractSocketPrivate::readNotification qabstractsocket_p.h 77 0x6cbff651
2 QAbstractSocketEngine::readNotification qabstractsocketengine.cpp 168 0x6cbb3855
3 QReadNotifier::event qnativesocketengine.cpp 1151 0x6cbcc009
4 QApplicationPrivate::notify_helper qapplication.cpp 4554 0xa8c020
5 QApplication::notify qapplication.cpp 3936 0xa89973
6 QCoreApplication::notifyInternal qcoreapplication.cpp 876 0x69dd3b76
7 QCoreApplication::sendEvent qcoreapplication.h 231 0x69e4488c
8 qt_internal_proc qeventdispatcher_win.cpp 484 0x69df92b7
9 USER32!IsWindowVisible C:\Windows\system32\user32.dll 0 0x76f3c4e7
10 QCharRef::operator= qstring.h 799 0x12112ca
11 USER32!IsWindowVisible C:\Windows\system32\user32.dll 0 0x76f3c5e7
12 qt_fast_timer_proc qeventdispatcher_win.cpp 428 0x69df90ec
13 USER32!IsWindowVisible C:\Windows\system32\user32.dll 0 0x76f3cc19
14 ?? 0
 
 

Никто не сталкивался с подобной проблемой?

PS: Забыл про один момент. Сервер в последнее время стал сыпать предупреждением
QSocketNotifier: Invalid socket specified
QSocketNotifier: Internal error
Не знаю, может быть это важно и относится к моей проблеме. Поковырявшись в исходниках QSocketNotifier, удалось выяснить, что Invalid socket specified - ошибка, появляющаяся при отрицательном и некорректном дескрипторе. Откуда он берется в QSocketNotifier - хрен его знает.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывае
Отправлено: _OLEGator_ от Октябрь 09, 2012, 12:30
На первый взгляд меня только смущает зануление элемента в списке, по нормальному его надо удалять из списка, а не просто занулять.
И в функции slotRejectClient() идет перебор этого массива, в котором отсутствует проверка mClients[ i ] на NULL, тут скорее всего и происходит обращение к зануленному элементу.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывае
Отправлено: Ground от Октябрь 09, 2012, 12:44
На первый взгляд меня только смущает зануление элемента в списке, по нормальному его надо удалять из списка, а не просто занулять.
И в функции slotRejectClient() идет перебор этого массива, в котором отсутствует проверка mClients[ i ] на NULL, тут скорее всего и происходит обращение к зануленному элементу.
Посыпаю голову пеплом, у меня фиксированный массив для сокетов. Ну это потом переделаю.
Причем массив не для сокетов, а для QPointer<QTcpSocket>. Поэтому строка:
Код
C++ (Qt)
mClients[i].data() != qobject_cast<QTcpSocket*>(sender())
должна корректно отрабатывать. Хотя в целях эксперимента я переписал строку вот так:
Код
C++ (Qt)
mClients[i].isNull() || mClients[i].data() != qobject_cast<QTcpSocket*>(sender())
Падения продолжаются.

Вот содержимое qabstractsocket.cpp на строке 672.
Код
C++ (Qt)
   // reset the read socket notifier state if we reentered inside the
   // readyRead() connected slot.
   if (readSocketNotifierStateSet && socketEngine &&
       readSocketNotifierState != socketEngine->isReadNotificationEnabled()) {
       socketEngine->setReadNotificationEnabled(readSocketNotifierState); // Тут у нас socketEngine = @0xfeeefeee
       readSocketNotifierStateSet = false;
   }
Эта функция отвечает за отправку сигнала readyRead(). Причем на 672 строку мы попадаем только в том случае, если уже находимся в слоте, зацепленном на readyRead() сокета. Все страньше и страньше. Я еще мог бы допустить такую ошибку, если бы у меня сокет осуществлял получение данных в одном потоке, а удалял я его в другом. Но в моем случае все действия происходят в одном потоке.
Дополнительно выяснилось, что socketEngine в момент падения имеет значение 0xfeeefeee. Согласно вики 0xfeeefeee -   Used by Microsoft's HeapFree() to mark freed heap memory.
У меня есть чувство, что память помечается освобожденной, но проверку, которая это должна выявить (if .. && socketEngine && ...) код все равно проходит. Видится мне, в *nix это было бы по-другому.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: _OLEGator_ от Октябрь 09, 2012, 13:45
а разве проверки mClients[ i ].data() и mClients[ i ] == NULL идентичны?
и программа не упадет на строке mClients[ i ].data(), если mClients[ i ] == NULL?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 13:50
а разве проверки mClients[ i ].data() и mClients[ i ] == NULL идентичны?
и программа не упадет на строке mClients[ i ].data(), если mClients[ i ] == NULL?
К mClients[ i ] == NULL идентичная проверка будет mClients.data() == Null или mClients.isNull()
Просто data() - это метод QPointer(), он не вызывает сегфолта в любом случае.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 09, 2012, 14:56
О да, сколько же я ловил у себя этот баг:)
Проблема в том(У меня так была) что вы делаете deleteLatter, потом эмитите сигнал emit signalSendTextToLog(QString("Клиент №%1 отключен!").arg(clientNumber)); это сиграл принимается кем-то. Этот кто-то делает действия(к примеру QCoreApplication::processEvents), которые приводят к тому, что ваш deleteLatter обработается до того как управление вернётся в int Server::disconnectClient(quint16 clientNumber). После этого управление возвращается в функцию внутри qabstractsocket но объект уже удалён.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 15:56
О да, сколько же я ловил у себя этот баг:)
Проблема в том(У меня так была) что вы делаете deleteLatter, потом эмитите сигнал emit signalSendTextToLog(QString("Клиент №%1 отключен!").arg(clientNumber)); это сиграл принимается кем-то. Этот кто-то делает действия(к примеру QCoreApplication::processEvents), которые приводят к тому, что ваш deleteLatter обработается до того как управление вернётся в int Server::disconnectClient(quint16 clientNumber). После этого управление возвращается в функцию внутри qabstractsocket но объект уже удалён.

И как вы решили данную проблему? Вполне может быть похожая ситуация и у меня, я правда попробовал вызывать deleteLater следующим образом:
Код
C++ (Qt)
QTimer::singleShot(1000, mClients[clientNumber].data(), SLOT(deleteLater()));
Но проблема все равно сохранилась, к сожалению.

UPD: Видимо я поторопился с выводами, если выставить 5000мс, сервер вроде бы работает стабильно


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 09, 2012, 16:05
Попробуйте.
emit signalSendTextToLog(QString("Клиент №%1 отключен!").arg(clientNumber));
до deleteLatter.

Удалять по таймеру, это жесть. Потом баги ловить будете ещё дольше.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 16:52
Попробуйте.
emit signalSendTextToLog(QString("Клиент №%1 отключен!").arg(clientNumber));
до deleteLatter.

Удалять по таймеру, это жесть. Потом баги ловить будете ещё дольше.
Я пробовал вообще убрать все сигналы после удаления, все равно не работает. Только с таймером. Выбор у меня не велик - либо оставлять сервер с утечками, либо удалять по таймеру.
Сейчас еще потестирую сервер на минимальном проекте, может еще какую-нибудь зависимость найду.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 09, 2012, 18:15
Вспомнил ещё подробности. У меня сигнал эмитился из слота, который присоединён к readyRead. Покажите код slotReceiveData().


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 23:04
Вспомнил ещё подробности. У меня сигнал эмитился из слота, который присоединён к readyRead. Покажите код slotReceiveData().

Вот это уже интереснее, т.к. ошибка в qabstractsocket.cpp связана с этим слотом. Вот код:
Код
C++ (Qt)
// Получение данных от клиента, обработка
int Server::slotReceiveData()
{
   bool result = false;
   int clientNumber = 0;
   // Поиск клиента, который выслал данных о MMF
   for (int i = 0; i < mMaxClients; i++)
   {
       if (mClients[i].data() != qobject_cast<QTcpSocket*>(sender()))
           continue;
       result = true;
       clientNumber = i;
       break;
   }
 
   if (!result)
       return -1;
 
   quint16 sizeOfPackage = 0;
   quint8 packageType = 0;
   QByteArray package;
 
   // Парсинг всех пришедших данных
   while (true)
   {
       // Считывание пришедших данных
       if (!mClients[clientNumber].isNull())
           mReceivedData[clientNumber].append(mClients[clientNumber].data()->readAll());
 
       // Проверка на условие: принято недостаточно данных
       if (mReceivedData[clientNumber].size() < (quint16)sizeof(quint16))
           break;
 
       // Получение количества отправленных байт (0 - 1 байты)
       memcpy(&sizeOfPackage,
              mReceivedData[clientNumber].data(),
              sizeof(quint16));
 
       // Проверка на условие: принят не весь пакет целиком
       if (mReceivedData[clientNumber].size() < sizeOfPackage || sizeOfPackage == 0)
           break;
 
       // Получение типа сообщения (2ой байт)
       memcpy(&packageType,
              mReceivedData[clientNumber].data() + sizeof(quint16),
              sizeof(quint8));
 
       // Получение основной части пакета
       const int specialData = sizeof(quint16) + sizeof(quint8);
       package = mReceivedData[clientNumber].mid(specialData,
                                                 sizeOfPackage - specialData);
 
       // Удаление распарсенных данных
       mReceivedData[clientNumber] = mReceivedData[clientNumber].mid(sizeOfPackage);
 
       // Обработка пакета
       switch (packageType)
       {
       case PackageType::Command:
           result = this->parseCommandPackage(clientNumber, package);
           break;
       case PackageType::RegInfo:
           result = this->parseRegInfoPackage(clientNumber, package);
           break;
       case PackageType::MMFName:
           result = this->parseMMFPackage(clientNumber, package);
           break;
       case PackageType::DescriptionRequest:
           break;
       case PackageType::CoefficientRequest:
           break;
       }
 
       if (!result)
           break;
   }
 
   if (!result)
       this->slotDisconnectClient(clientNumber);
 
   return 0;
}


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 09, 2012, 23:26
Пока не берусь говорить со 100% уверенностью, но кажется, с вашей помощью, я нашел причину падений.
Вы были правы, когда говорили об некорректной обработке очереди сообщений. Сегодня утром вспомнил про один хинт, который я добавил давно, чтобы избежать фриза UI:
Код
C++ (Qt)
// Запись информации в журнал
int MainWindow::slotAddTextToLog(const QString text)
{
   teLog->append(QDate::currentDate().toString("dd.MM.yyyy") +
                 " " +
                 QTime::currentTime().toString() + " - " + text);
 
   QCoreApplication::processEvents(); // Вот беда, судя по всему этот вызов обрабатывает не только UI-сообщения. Видимо в нем и была проблема
 
   return 0;
}

Пока еще потестирую и обязательно отпишусь.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 07:11
Все протестировал, все работает. Остался один нерешенный вопрос.
Я из слота, работающего с UI (вывод текста) удалил следующую строку:
Код
C++ (Qt)
QCoreApplication::processEvents();
По хорошему нужно сделать так, чтобы обрабатывались все события, кроме DeferredDelete. Но судя по документации, такой возможности совсем нет. Причем даже непонятно, к какому классу событий относится DeferredDelete. Вот оттуда цитата:
Код:
QEventLoop::AllEvents - All events. Note that DeferredDelete events are processed specially. See QObject::deleteLater() for more details.
QEventLoop::ExcludeUserInputEvents - Do not process user input events, such as ButtonPress and KeyPress. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeUserInputEvents flag.
QEventLoop::ExcludeSocketNotifiers - Do not process socket notifier events. Note that the events are not discarded; they will be delivered the next time processEvents() is called without the ExcludeSocketNotifiers flag.

Как же все таки можно исключить из обработки событие удаления объекта?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: LisandreL от Октябрь 10, 2012, 07:55
Ну давайте посмотрим. Само по себе от удаления объекта ничего упасть не может. Должно быть обращение к нему (ну или к подобъектам, если судить по стеку).
Значит что? Что мы хотим в нашем processEvents мы не хотим допустить обращения к сокету.
Какой флаг нам нужен? Правильно, QEventLoop::ExcludeSocketNotifiers.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 08:14
Лучше создайте 2 сигнала
signalSendTextToLogPrivate(QString);
signalSendTextToLog(QString);
и законнектите их друг к другу вот так
connect(this, SIGNAL(signalSendTextToLogPrivate(QString)) , this, SIGNAL(signalSendTextToLog(QString)), Qt::QueuedConnection)

В Server::disconnectClient делайте
emit signalSendTextToLogPrivate(QString("Клиент №%1 отключен!").arg(clientNumber));
в итоге все кто приконнекчен к signalSendTextToLog получат управление после выхода из ваших слотов.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 08:20
Ну давайте посмотрим. Само по себе от удаления объекта ничего упасть не может. Должно быть обращение к нему (ну или к подобъектам, если судить по стеку).
Значит что? Что мы хотим в нашем processEvents мы не хотим допустить обращения к сокету.
Какой флаг нам нужен? Правильно, QEventLoop::ExcludeSocketNotifiers.
Проблема в том, что может быть сложно контролировать все слоты подключённые к signalSendTextToLog (или другим сигналам, которые мы будем эмитить). В итоге эти грабли будут всплывать опять и опять.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 10:28
В итоговом решение объединил два совета. Сделал 2 сигнала и соединил их через Qt::QueuedConnection, а в слоте работы с UI добавил processEvent с параметром QEventLoop::ExcludeSocketNotifiers.
Если применять первое решение с 2 сигналами - программа падает и по стеку вызов видно, что ошибка где-то в moc-файле. Я там уже разбираться не стал. Само падение происходит при обращении к параметру сигнала text.
Если применять второе решение с QEventLoop::ExcludeSocketNotifiers - программа работает стабильно, UI не виснет, но сервер частенько бросает предупреждения вида:
Код
C++ (Qt)
QSocketNotifier: Invalid socket specified
QSocketNotifier: Internal error
Что, в принципе, логично, учитывая, что мы удаляем объект, а потом только обрабатываем уведомления от сокета. Если же объединить два варианта, сервер ведет себя стабильно, при полной загрузке работает уже час без сбоев.
В случае же, если при увеличении нагрузке вылезет подобный баг - либо полезу разбираться дальше глубже, в причине проблемы, либо просто уменьшу вывод информации в лог сервера и отключу функцию processEvents(). Одним словом, теперь я знаю где искать :)

Вопрос можно считать закрытым. Спасибо всем, кто помогал. И отдельное, большое человеческое спасибо, Patrin Andrey, сам бы вряд-ли докопался до истинной причины бага :)


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 10:31
Я не думаю что ты докопался до истинной причины бага. Тебе не приходила в голову мысль, что раз приходится писать такие странные костыли для несложного, в общем-то, действия, то у тебя что-то сильно не так с подходом?

З.Ы. По теме не мог помочь, потому что код, на мой взгляд, странный весь


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 10:32
З.Ы. По теме не мог помочь, потому что код, на мой взгляд, странный весь
Ну так вы подскажите, в чем странность?

Плюс я бы не сказал, что это костыли. Точнее не так. Если уж говорить про костыли, то это - processEvents(). Не было бы этой функции, код отрабатывал бы нормально. А чтобы ее не было - нужно было изначально делать сервер многопоточным.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 10:36
З.Ы. По теме не мог помочь, потому что код, на мой взгляд, странный весь
Ну так вы подскажите, в чем странность?

На мой взгляд странность во всем. Например, disconnect не нужен. Не удалять пустой указатель из списка ммм... необычно. Какой смысл в QPointer, если ты вручную управляешь жизнью сокета? Зачем делать close(), если сокет и так уже отключился?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 10:50
З.Ы. По теме не мог помочь, потому что код, на мой взгляд, странный весь
Ну так вы подскажите, в чем странность?

На мой взгляд странность во всем. Например, disconnect не нужен. Не удалять пустой указатель из списка ммм... необычно. Какой смысл в QPointer, если ты вручную управляешь жизнью сокета? Зачем делать close(), если сокет и так уже отключился?

Ну я же тоже это все не просто написал.
disconnect() в данном случае нужен для того, чтобы во время удаления сокета не получить другой сигнал, на прием/чтение данных. Ведь согласно документации, автоотключение происходит лишь при уничтожении объекта. А сколько пройдет до этого момента? Я уж лучше отключу сигнал вручную. Были у меня случаи с таким багом.
close() нужен для тех случаев, когда disconnectClient() вызывается не из слота rejectClient, а, допустим, во время остановки сервера. В close() стоит проверка, не находится ли сокет в состоянии закрытия, так что мы ничего не теряем при вызове этой функции, зато уменьшаем объем нашего кода.
Насчет не удаления пустого указателя - я голову уже пеплом выше посыпал, у меня фиксированный массив клиентов. Т.е. в конфиге написали - 512, лимит - 512.
И где я управляю жизнью сокета? То, что я его зануляю? Это привычка, и я не назову ее плохой. Пусть уж лучше у меня будет нулевой указатель и QPointer со значением NULL, чем указатель непонять куда и опять же QPointer == NULL. В обоих случаях же, сокет уведомит QPointer о своем разрушении. В общем, мне так спокойнее.

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


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 10:54
Для Patrin Andrey они не являются критическими, а вот для меня являются. Я не хочу критиковать твой код, а хочу обратить внимание на то, что создание таких костылей, которыми является итоговое решение, может быть результатом неправильного подхода к написанию сервера


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 11:03
Для Patrin Andrey они не являются критическими, а вот для меня являются. Я не хочу критиковать твой код, а хочу обратить внимание на то, что создание таких костылей, которыми является итоговое решение, может быть результатом неправильного подхода к написанию сервера

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

Ваш пример с QPointer - это дело личного вкуса, к серверу же не относится.
С ручным отключением сигналов. Ну я вот так прикинул, можно было в самом слоте получения данных сделать проверку, находится ли сокет в состоянии Connected. Это вы считаете правильным решением приведенной мной проблемы?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 11:07
Блин... Придется удалить одно из своих сообщений, видимо...

Еще раз. То, что я перечислил - это моменты, мешающие лично мне ответить на вопрос "почему SegFault и как его избежать?". Что может быть не так с твоим подходом я не знаю как минимум по двум причинам:
1) не видно всего кода, а есть только кусок с удалением
2) не факт что я сам знаю правильных подход, но костыльность решения намекает что он может отличаться от твоего.

Так понятно?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 11:11
Блин... Придется удалить одно из своих сообщений, видимо...

Еще раз. То, что я перечислил - это моменты, мешающие лично мне ответить на вопрос "почему SegFault и как его избежать?". Что может быть не так с твоим подходом я не знаю как минимум по двум причинам:
1) не видно всего кода, а есть только кусок с удалением
2) не факт что я сам знаю правильных подход, но костыльность решения намекает что он может отличаться от твоего.
Так понятно?
Более чем.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Пантер от Октябрь 10, 2012, 11:12
Я согласен с mutineer - код оставляет желать лучшего. Допустим, у меня в одной проге вот так сделано:
Код
C++ (Qt)
void SocketListener::addConnection_p (QTcpSocket *socket)
{
connect (socket, SIGNAL (readyRead()), SLOT (readyRead()));
connect (socket, SIGNAL (disconnected()), SLOT (disconnected()));
 
sockets_.push_back ( {socket, false});
qDebug () << "New connection" << socket->peerAddress ();
}
 
void SocketListener::readyRead()
{
QTcpSocket *socket = qobject_cast <QTcpSocket *> (sender ());
Q_CHECK_PTR (socket);
 
do {
const QString reply = Utils::readLineFromSocket (socket);
 
if (!replyAnalizer_.setReply (reply, socket)) {
qWarning () << "Socket reply is incorrect" << reply;
socket->disconnectFromHost ();
}
} while (socket->canReadLine ());
}
 
void SocketListener::disconnected()
{
QTcpSocket *socket = qobject_cast <QTcpSocket *> (sender ());
Q_CHECK_PTR (socket);
 
qDebug () << "Closed connection" << socket->peerAddress ();
 
socket->deleteLater ();
sockets_.erase (findSocket (socket));
}
 
SocketListener::Sockets::iterator SocketListener::findSocket (QTcpSocket *socket)
{
return std::find_if (sockets_.begin(),
sockets_.end(),
[socket] (const Socket & s) -> bool {
return s.first == socket;
}
);
}
 


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 11:34
Я не думаю что ты докопался до истинной причины бага.
Истинная причина бага - то что во время обработки readyRead сокет удаляется. А чтобы этого не происходило нужно исключить исполнение кода, который может выполнить такие действия(совершенно об этом не подозревая)

PS. по поводу кода - мне он тоже не нравится.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 11:36
Я не думаю что ты докопался до истинной причины бага.
Истинная причина бага - то что во время обработки readyRead сокет удаляется. А чтобы этого не происходило нужно исключить исполнение кода, который может выполнить такие действия(совершенно об этом не подозревая)

Какая может быть обработка readyRead, если для удаления сокет должен перейти в disconnected состояние и никаких данных получать уже не может?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 11:52
Упс, только что заметил что в slotReceiveData вызывается slotDisconnectClient а не disconnectClient(отличные названия, блин). Если из  slotDisconnectClient не вызывается disconnectClient то ниженаписанное можно не читать, а если вызывается, то:
в slotReceiveData()
if (!result)
   this->slotDisconnectClient(clientNumber);
а там deleteLatter для сокета(фактически внутри ReadyRead). теперь достаточно чтобы лишь дать выполниться этому делету, и всё - мы внутри readyRead удалённого сокета.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 11:55
А зачем вообще вызывать удаление сокета из чтения данных?


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 12:00
Спроси у автора. Но такая ситуация может возникныть и без ручного удаления. Если мы из readyread вызываем QCoreApplication::processEvents а в это время клиент дисконнектится, то получится то же самое.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 12:05
Ну так значит сама идея вызова delete или processEvents из readyRead неправильна


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Patrin Andrey от Октябрь 10, 2012, 12:10
Так я о чём и говорю. Но как видим processEvent может вызваться из слота, который приконнекчен к сигналу, который эмитится в readyRead. А это уже контролировать очень сложно(можно конечно не эмитить сигналов:)). Поэтому если уж и эмитить сигналы из readyRead, то нужно обеспечить их присоединение через Qt::QueuedConnection.


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: mutineer от Октябрь 10, 2012, 12:12
Вот такую кашу вызовов неконтролируемо откуда я и имел в виду под неправильным подходом


Название: Re: Проблема с сервером (deleteLater() для сокета вызывает SegFault)
Отправлено: Ground от Октябрь 10, 2012, 12:14
Вот такую кашу вызовов неконтролируемо откуда я и имел в виду под неправильным подходом
Вот теперь все намного понятнее :) Но ей богу, пока сам на такие грабли не наступишь - нормально писать не станешь.