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

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

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Проблема с сервером (deleteLater() для сокета вызывает SegFault)  (Прочитано 18618 раз)
Patrin Andrey
Гость
« Ответ #15 : Октябрь 10, 2012, 08:20 »

Ну давайте посмотрим. Само по себе от удаления объекта ничего упасть не может. Должно быть обращение к нему (ну или к подобъектам, если судить по стеку).
Значит что? Что мы хотим в нашем processEvents мы не хотим допустить обращения к сокету.
Какой флаг нам нужен? Правильно, QEventLoop::ExcludeSocketNotifiers.
Проблема в том, что может быть сложно контролировать все слоты подключённые к signalSendTextToLog (или другим сигналам, которые мы будем эмитить). В итоге эти грабли будут всплывать опять и опять.
Записан
Ground
Гость
« Ответ #16 : Октябрь 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, сам бы вряд-ли докопался до истинной причины бага Улыбающийся
Записан
mutineer
Гость
« Ответ #17 : Октябрь 10, 2012, 10:31 »

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

З.Ы. По теме не мог помочь, потому что код, на мой взгляд, странный весь
Записан
Ground
Гость
« Ответ #18 : Октябрь 10, 2012, 10:32 »

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

Плюс я бы не сказал, что это костыли. Точнее не так. Если уж говорить про костыли, то это - processEvents(). Не было бы этой функции, код отрабатывал бы нормально. А чтобы ее не было - нужно было изначально делать сервер многопоточным.
« Последнее редактирование: Октябрь 10, 2012, 10:37 от Ground » Записан
mutineer
Гость
« Ответ #19 : Октябрь 10, 2012, 10:36 »

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

На мой взгляд странность во всем. Например, disconnect не нужен. Не удалять пустой указатель из списка ммм... необычно. Какой смысл в QPointer, если ты вручную управляешь жизнью сокета? Зачем делать close(), если сокет и так уже отключился?
Записан
Ground
Гость
« Ответ #20 : Октябрь 10, 2012, 10:50 »

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

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

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

Сейчас вы указали лишь некоторые кода недостатки (для вас), которые вряд ли являются критическими для восприятия проблемы в целом.
« Последнее редактирование: Октябрь 10, 2012, 10:53 от Ground » Записан
mutineer
Гость
« Ответ #21 : Октябрь 10, 2012, 10:54 »

Для Patrin Andrey они не являются критическими, а вот для меня являются. Я не хочу критиковать твой код, а хочу обратить внимание на то, что создание таких костылей, которыми является итоговое решение, может быть результатом неправильного подхода к написанию сервера
Записан
Ground
Гость
« Ответ #22 : Октябрь 10, 2012, 11:03 »

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

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

Ваш пример с QPointer - это дело личного вкуса, к серверу же не относится.
С ручным отключением сигналов. Ну я вот так прикинул, можно было в самом слоте получения данных сделать проверку, находится ли сокет в состоянии Connected. Это вы считаете правильным решением приведенной мной проблемы?
Записан
mutineer
Гость
« Ответ #23 : Октябрь 10, 2012, 11:07 »

Блин... Придется удалить одно из своих сообщений, видимо...

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

Так понятно?
Записан
Ground
Гость
« Ответ #24 : Октябрь 10, 2012, 11:11 »

Блин... Придется удалить одно из своих сообщений, видимо...

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

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #25 : Октябрь 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;
}
);
}
 
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Patrin Andrey
Гость
« Ответ #26 : Октябрь 10, 2012, 11:34 »

Я не думаю что ты докопался до истинной причины бага.
Истинная причина бага - то что во время обработки readyRead сокет удаляется. А чтобы этого не происходило нужно исключить исполнение кода, который может выполнить такие действия(совершенно об этом не подозревая)

PS. по поводу кода - мне он тоже не нравится.
« Последнее редактирование: Октябрь 10, 2012, 11:38 от Patrin Andrey » Записан
mutineer
Гость
« Ответ #27 : Октябрь 10, 2012, 11:36 »

Я не думаю что ты докопался до истинной причины бага.
Истинная причина бага - то что во время обработки readyRead сокет удаляется. А чтобы этого не происходило нужно исключить исполнение кода, который может выполнить такие действия(совершенно об этом не подозревая)

Какая может быть обработка readyRead, если для удаления сокет должен перейти в disconnected состояние и никаких данных получать уже не может?
Записан
Patrin Andrey
Гость
« Ответ #28 : Октябрь 10, 2012, 11:52 »

Упс, только что заметил что в slotReceiveData вызывается slotDisconnectClient а не disconnectClient(отличные названия, блин). Если из  slotDisconnectClient не вызывается disconnectClient то ниженаписанное можно не читать, а если вызывается, то:
в slotReceiveData()
if (!result)
   this->slotDisconnectClient(clientNumber);
а там deleteLatter для сокета(фактически внутри ReadyRead). теперь достаточно чтобы лишь дать выполниться этому делету, и всё - мы внутри readyRead удалённого сокета.
Записан
mutineer
Гость
« Ответ #29 : Октябрь 10, 2012, 11:55 »

А зачем вообще вызывать удаление сокета из чтения данных?
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


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