Здравствуйте.
Я понимаю что вопрос будет звучать глупо, но все-же...
Пишется многопоточное приложение. Потоки ходят по серверам и проверяют их работоспособность. Все работает замечательно до...
Подсовываем одному из потоков невалидное имя. gethostbyname() возвращает NULL и дальше... остальные соединения начинают работать вразлад - когда коннектятся, когда нет... Причем имена разрезолвливаются, отказы идут на вызове connect().
Все вызовы стандартные блокирующие, вроде почти академическая задача... Почему возникают такие проблемы непонятно.
Не нужно ли как-то дполнительно сбрасывать/переинициаизировать ядро после неудачного резолва имени?С уважением,
Сергей
>Не нужно ли как-то дполнительно сбрасывать/переинициаизировать ядро после неудачного резолва имени?Очевидно нет. И при чём тут вообще ядро? Вероятно причина кроется где-то вне описанных вами исходных данных.
>>Не нужно ли как-то дполнительно сбрасывать/переинициаизировать ядро после неудачного резолва имени?
>
>Очевидно нет. И при чём тут вообще ядро? Вероятно причина кроется где-то
>вне описанных вами исходных данных.Я же сказал что вопрос выглядит глупым. Но больше идей нет.
Пример сейчас. 4 потока. Каждый лезет на свой адрес. www.yandex.ru www.google.com www.google.com и www.microsoft.com
По каждому потоку - открывает сокет, резолвит имя, открывает соединениек на порт 80, посылает HTTP-запрос, читает HTTP-ответ, закрывает сокет.Пока все имена валидные - все хорошо. Как только меняю (динамически) имя для второго потока на www.xgoogle.com ( канселим тред, запускаем новый ) -наступает разнобой.
Тогда вопрос так поставим. По умолчанию ВРОДЕ при pthread_cancel() тред останавливается и все его handle освобождаются. Подразумевается что закрывается сокет?
А как обработка идет если тред в это время заблокирован gethostbyname() или connect()? Сделать параллельно shutdown() на этот же сокет? А можно разве так?Сергей.
PS. А если обрабатывать - какой сигнал? SIG... ??
>Тогда вопрос так поставим. По умолчанию ВРОДЕ при pthread_cancel() тред останавливается и
>все его handle освобождаются. Подразумевается что закрывается сокет?Нет. У thread нет сокетов, только у процесса.
>А как обработка идет если тред в это время заблокирован gethostbyname() или
>connect()?В зависимости от реализации. ЧИтай мануалы.
обычно pthread_cancel останавливает только когда поток в хорошем состоянии (cancellation point)
>Подсовываем одному из потоков невалидное имя. gethostbyname() возвращает NULL и дальше... остальные
>отказы идут на вызове connect().Какие такие отказы? ))
>>Подсовываем одному из потоков невалидное имя. gethostbyname() возвращает NULL и дальше... остальные
>>отказы идут на вызове connect().
>
>Какие такие отказы? ))Я не разбирался в кодах ошибок. Просто сам факт - до этого все вызовы connect() возвращали значение =0 (успешное завершение), теперь стали <0 (как говорится если хотите - смотрите код ошибки).
Интересен не код - почему послали. Интересен и непонятен сам факт - почему на факт неудачного резолва имени перестает корректно работать сам стек. Или (см мой ответ выше) некорректно что-то канселится????
>Я не разбирался в кодах ошибок.А вот и разберись, будет понятнее.
>Интересен не код - почему послали.
этот код и есть причина!! Детский сад штаны на лямках.
>почему на факт неудачного резолва имени перестает корректно работать сам стек.
1. Обрабатывай ошибки всез системных вызовов.
2. Запусти программу в valgrind
3. Запусти программу в отладчике, проверь как работают функции.>Или (см мой ответ выше) некорректно что-то канселится????
Прерывать потоки глупо практически всегда.
>>Интересен не код - почему послали.
>этот код и есть причина!! Детский сад штаны на лямках.Ну хорошо - код ошибки как правило CONNECTION REFUSED
Почему? Кто? За что?>>Или (см мой ответ выше) некорректно что-то канселится????
>Прерывать потоки глупо практически всегда.А как их корректно завершать? Проверять постоянно какой-то флаг? Но это неправильно.
Обрабатывать сигналы? Но обработка сигнала идет в контексте потока? Если из обработчика сигнала я скажу pthread_exit() - это будет корректно?Сергей
PS. Спасибо за ответы, помогает местами.
>Ну хорошо - код ошибки как правило CONNECTION REFUSED
>Почему? Кто? За что?На другом конце тебя не ждут. Наверно адрес не правильный )) Наверно ты адрес из имени не правильно получаешь. Как ты это делаешь?
>А как их корректно завершать?
Правильно вообще не завершать. Поток отработал и завершился сам, или встал в очередь за новым заданием. Разумеется не должен зависать ))
>Проверять постоянно какой-то флаг? Но это неправильно.
Это правильнее чем внешнее прерывание потока, которое потенциальная утечка ресурсов и не может использоваться для управления работой, максимум обработка ошибок.
>Обрабатывать сигналы?
Сигналы как и сокеты - принадлежат процессу. Посылать сигналы потокам было возможно используя недокументированные возможности некоторых реализаций. Надеятся на такую возможность глупо.
>>Ну хорошо - код ошибки как правило CONNECTION REFUSED
>>Почему? Кто? За что?
>На другом конце тебя не ждут. Наверно адрес не правильный )) Наверно
>ты адрес из имени не правильно получаешь. Как ты это делаешь?struct hostent * hp;
struct sockaddr_in sAddr;
...
if( NULL == (hp = gethostbyname( strBuff )) )
{
error( ... );
goto finish;
}
memset( &sAddr, 0, sizeof( sAddr ) );
memcpy( &sAddr.sin_addr, hp->h_addr, hp->h_length );
sAddr.sin_family = hp->h_addrtype;
sAddr.sin_port = htons(req->n_PortNumber);
if( 0 > connect( req->socket, (struct sockaddr*) &sAddr, sizeof(sAddr) ) )
{
error( ... );
goto finish;
}
...>>А как их корректно завершать?
>Правильно вообще не завершать. Поток отработал и завершился сам, или встал в
>очередь за новым заданием. Разумеется не должен зависать ))
>>Проверять постоянно какой-то флаг? Но это неправильно.
>Это правильнее чем внешнее прерывание потока, которое потенциальная утечка ресурсов и не
> может использоваться для управления работой, максимум обработка ошибок.Вот пример. Процесс обработки довольно длительный и состоит из нескольких этапов. Хочется его прервать (прекратить) в любой момент. Аппаратно для этого есть прерывания. Из которого управление можно и вернуть процессу(треду), а можно кому другому отдать. А как управлять одним тредом из другого? Прекращать по желанию, корректно освобождая ресурсы завершаемого треда? Ставить по всему телу процесса проверку флага?
Фактически это то-же аппарат исключения, событийного исключения, только событие происходит не из самого процесса, а снаружи.Сергей.
> if( NULL == (hp = gethostbyname( strBuff )) )Не надо пользоваться gethostbyname в многопоточной программе. Она использует статический буфер для результата. И если её вызвать из двух потоков одновременно данные будут испорчены. Ты соединялся не с теми. но пока резолвилось нормально ты не замечал ошибку и не знал об этом ))
Используй gethostbyname_r, а лучше getaddrinfo. В однопоточной программе тоже.
>Вот пример. Процесс обработки довольно длительный и состоит из нескольких этапов.
>Хочется >его прервать (прекратить) в любой момент.А как будешь освобождать ресурсы? А сохранять промежуточные результаты? А возвращать в корректное состояние?
Прервал - и всё чудом вернулось как и было, или даже нет, все правильные изменения остались, а всё недоделанное откатилось )) Это чудо похлеще деда мороза.Хочется - это не аргумент.
Альтернатива потоку проверять не пора ли освободить ресурсы и завершать работу - это писать в некий журнал все ресурсы, и потом тот кто завершает работу потока их освобождает. Но опять же, тебе интересно блокировка потока вызывающего завершение другого потока до тех пор пока он не завершится?
Удобных мест для проверки в долгих задачах полно, не понимаю в чём проблема ))
А в коротких - прерывать нет смысла, быстрее задача завершится.PS: А вообще для твоей задачи потоки не нужны ))
>Не надо пользоваться gethostbyname в многопоточной программе. Она использует статический буфер для
>результата. И если её вызвать из двух потоков одновременно данные
>будут испорчены. Ты соединялся не с теми. но пока резолвилось нормально
>ты не замечал ошибку и не знал об этом ))
>
>Используй gethostbyname_r, а лучше getaddrinfo. В однопоточной программе тоже.Спасибо, ошибку понял!
>>Вот пример. Процесс обработки довольно длительный и состоит из нескольких этапов.
>>Хочется >его прервать (прекратить) в любой момент.
>А как будешь освобождать ресурсы? А сохранять промежуточные результаты? А возвращать в
>корректное состояние?
>Прервал - и всё чудом вернулось как и было, или даже нет,
>все правильные изменения остались, а всё недоделанное откатилось )) Это чудо
>похлеще деда мороза.В SQL-серверах это называется транзакции. Они или комитятся или rollback :) Хочется аналогичное и по тредам :)
>Хочется - это не аргумент.
Ну согласен, но с другой строны, если бы не было желания, никто ничего бы и не делал :)
>PS: А вообще для твоей задачи потоки не нужны ))
Заказчик хочет так. Кстати я не знаю что лучше для многих (неск. тысяч) запросов по разным адресам - много тредов или большие списки в select?
>
>Заказчик хочет так. Кстати я не знаю что лучше для многих (неск.
>тысяч) запросов по разным адресам - много тредов или большие списки
>в select?Несколько threads и select. Создавать пару тысячь тредов - идиотизм.
>[оверквотинг удален]
>работает замечательно до...
>Подсовываем одному из потоков невалидное имя. gethostbyname() возвращает NULL и дальше... остальные
>соединения начинают работать вразлад - когда коннектятся, когда нет... Причем имена
>разрезолвливаются, отказы идут на вызове connect().
>Все вызовы стандартные блокирующие, вроде почти академическая задача... Почему возникают такие проблемы
>непонятно.
>Не нужно ли как-то дполнительно сбрасывать/переинициаизировать ядро после неудачного резолва имени?
>
>С уважением,
>СергейМожет Вам стоит попробовать вместо gethostbyname() использовать gethostbyname_r().
А у меня другая проблема. Использую чужой код, который по идее должен работать как сокет-сервер. В hostname - char имя хоста или IP, в port - номер порта.
sockaddr_in service;
memset(&service, 0, sizeof(sockaddr_in));
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(hostname);if (service.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *host = gethostbyname(hostname);
if (host == NULL || host->h_addr == NULL)
{
printf( "Problem accessing the DNS. (addr: %s)", hostname);
return false;
}
service.sin_addr = *(struct in_addr*)host->h_addr;
}
service.sin_port = htons(port);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socket != -1)
{
if (connect(m_socket, (sockaddr*) &service, sizeof(struct sockaddr)) < 0)
{
printf( "%s, failed to connect socket. Error: %d\n", " ", GetSockError());
perror("connect");
}
}
m_socket получает значение, однако при вызове connect получаю сообщение 61 Connection refused.
>А у меня другая проблема. Использую чужой код, который по идее должен
>работать как сокет-сервер. В hostname - char имя хоста или IP,
>в port - номер порта.
> struct hostent *host = gethostbyname(hostname);[skip]
> if (connect(m_socket, (sockaddr*) &service, sizeof(struct sockaddr)) < 0)
[skip]
>m_socket получает значение, однако при вызове connect получаю сообщение 61 Connection refused.
>У меня проблема решилась использованием gethostbyname_r() т.к. gethostbyname() работает со статическим внутренним буфером и требы могут портить его друг у друга.
А вообще не вредно посмотреть промежуточно что и как отдается как адрес для соединения.
>
>У меня проблема решилась использованием gethostbyname_r() т.к. gethostbyname() работает со статическим внутренним
>буфером и требы могут портить его друг у друга.
>
>А вообще не вредно посмотреть промежуточно что и как отдается как адрес
>для соединения.Если я испльзую не имя, а адрес и тогда gethostbyname не срабатывает, то та же ошибка с connect, адрес у меня "127.0.0.1", превращается в значение 16777343 для service.sin_addr.s_addr.