Отказ работы socket API
От: Ketsokal  
Дата: 08.02.05 15:49
Оценка:
Здравствуйте.
Есть серверное приложение, каждому новому клиенту выделяется свой собственный поток. Испльзуются неблокирующие ассинхронные сокеты из библиотки ws2_32.dll (API, никаких MFC или иных классов), а так же механизм обработки сетевых событий. И вот, иногда возникают моменты, когда при выполнении функций send и, что интересно, closesocket, поток зависает (выход из функции не происходит), процессор выдает загрузку 100%, а серверная часть отказывается принимать новые соединения. При этом все присоединенные клиенты продолжают нормально работать. Создать искусственные условия возникновения ошибки никак не удается, однако прослеживается определенная закономерность: при возрастании количества активных клиентов, увеличивается шанс возникновения такой ситуации (в случае, когда их больше 100, программа не живет и дня).

Однажды все-таки удалось смоделировать одну из ситуаций, которая приводила к зависанию. Клиент отсылал сообщение серверу, после чего соединение внезапно разрывалось со стороны клиента. Эвент, получив уведомление о поступивших данных срабатывал и сервер начинал обработку пакета. Пакет, оказывался, например, устаревшим для текущего протокола и клиенту отправлялось уведомительное сообщение о невозможности обработать пакет. Но так как соединение уже потеряно, при попытке отправить пакет поток повисал на send'е. Но ведь в таких случаях send должен вернуть ошибку? Этот момент решил проверкой, не поступил ли FD_CLOSE перед каждом send'ом. Но проблема до конца все равно не решилась, хотя случаи повисания и стали более редкими.

Пробовал изменить механизм обслуживания: отказался от эвентов, стал использовать циклы и select — проблема не исчезла, даже хуже: случаи участились. Запихал send и closesocket в SEH-блок — проблема не исчезла, однако, с шансом 50/50 она либо тихо аварийно (без сообщения об ошибке) завершалась, либо привычно висла с полной загрузкой процессора.

Перечитал кучу различной информации о сокетах, начиная от MSDN и кончая каким-то официальным описанием — не нашел никакого намека на решение проблемы. Закралось подозрение, что использую, сам того не ведая блокирующие сокеты (хотя в описании указано, что WSAEventSelect автоматически переводит сокет в неблокирующий режим), вручную помечал новый сокет как неблокирующий — непомогло.

Оказавшись в тупике, на всякий случай полностью поменял механизм работы с потоками, пул потоков, перепроверил логику работы с ними... Менял тип приложения (GUI на консоль). Проблема упорно отказывается решаться. Не представляю что еще можно сделать. Если кто-то с подобным тоже встречался...

Тестировалось все это дело на Windows NT 4.0, Windows 2000 Server, Windows 2003. При разработке использовался компилятор MS Visual C++ 6.0 и 7.0 (версию тоже в надежде на чудо менял). Если будут необходимы участки кода — могу предоставить.
Re: Отказ работы socket API
От: Irokez  
Дата: 08.02.05 18:15
Оценка:
Здравствуйте, Ketsokal, Вы писали:

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

Ниже чисто предложения, возможно что-то окажеться лишним.

1) проще обрабытвать состояния сокетов, через select.
2) Перед приемом сообщения, даже в случае срабатываняи события FD_READ, попробуйте прочитать количество байт в буфере приема getsockopt.
3) Перед отправкой сообщения, пробывать отправлять токо тогда, когда есть событие FD_SEND.
4) В асинхронной приемопередаче есть ситуация, когда сокет выкидывает ошибку, не помню код, но суть в том, что данные не могут быть отправлены немедленно, небходимо подождать и повторить попытку.
5) Ну и на всякий случай проверить не убивается ли где стэк, не пшиеться ли в NULL.

А ваще еслиб глянуть на кусок кода потоков, былоб лучше
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.