Mikl
Постоялец
Offline
|
|
« : 03-07-2009 17:56 » |
|
Уважаемые, подскажите ЕСЛИ кто сталкивался. Есть набор клиент-серверных приложений собственной разработки. Для связи используются сокеты в виде классов в которых реализовна и клиентская и серверная части. Вся часть ПО ответственная за связь вынесена в отдельную DLL. Сервер запускается, клиенты к нему подключаются, отключаются и т.д. и т.п. Все нормально работало неделями - дольше не получалось тестировать непрерывно - приходилось перезапускать. Клиентов было штук 20. Сейчас клиентов увеличил до 150. Теперь все работает но 2,5 дня. Может и дольше - до недели, но не меньше точно. Потом происходит следующая ситуация. Сервер видит подключения клиентов, новые подключаются, старые отключаются, но данные не пересылаются ни в одну сторону. Была идея - сделать перезапуск класса сервера, отвечающего за связь. Обнаружил интересную штуку - если сделать этот перезапуск искусственно - скажем через 1 сутки - все нормально работает. Но если дождаться когда связь "отвалится" сама - увы, клиенты подключаются, а данные не ходят ни в одну сторону. Перезапускаешь приложение - все опять работает. Понятно, что где-то что-то переполняется, но вот что и где? Идеи есть какие-нибудь? Спасибо.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #1 : 03-07-2009 18:10 » |
|
А есть ли освобождение сокетов на серверной части, после окончание сессии с клиентом?
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #2 : 03-07-2009 18:14 » |
|
Да, конечно. Потом, даже если бы и не было - перезапуск класса связи - это delete, затем new со всеми вытекающими. В конструкторе/деструкторе класса используются фукнции WSAStartup/WSACleanup. Так что с МОЕЙ точки зрения - все начинается с нуля с перезапуском класса связи. НО я где-то ошибаюсь.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #3 : 03-07-2009 18:17 » |
|
Mikl, не пробовал ли прогонять свое приложение через программы по отлову утечек и тому подобное?
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #4 : 03-07-2009 18:19 » |
|
Нет. Не силен я в этом. В Visual Stiduo есть механизм контроля утечек памяти и протоколирования всех исключений. Вот его я задействовал - там чисто.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #5 : 03-07-2009 18:31 » |
|
Хорошо. С другой стороны зайдем. Когда твое приложение зависло. В диспетчере задач. Сколько памяти выделено для твоего приложения. Также, сколько нитей? Также эти цифри в рабочем состоянии.
Также программа netstat. Сколько открытых сокетов показано, которые принадлежат твоему приложению?
Кстати, у Виндовс есть лимит открытых сокетов. И по умолчанию он довольно маленький. Не превышаеш ли ты его?
|
|
« Последнее редактирование: 03-07-2009 18:34 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #6 : 03-07-2009 18:35 » |
|
Приложение НЕ зависло. Оно как работало - так и работает. Срабатывают функции перезапуска модуля связи. Дальше я жду - ничего нет (в смысле данные не идут) и нажимаю в приложении кнопку выход - все КОРРЕКТНО завершается без каких-либо утечек чего-либо . Памяти сколько выделено и нитей - столько же, сколько и при старте программы. А вот насчет netstat - сказать ничего не могу - я только что про нее узнал - пока есть данные только ДО падения связи. Жду когда упадет - с учетом запланированного на завтра перезапуска - где-то в понедельник, не раньше. Добавлено через 1 минуту и 15 секунд:Да, еще забыл добавить интересное наблюдение - перезапуск класса связи искусственно срабатывает 101 раз. А потом надо один фиг перезагружать всю программу целиком.
|
|
« Последнее редактирование: 03-07-2009 18:36 от Mikl »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #7 : 03-07-2009 18:43 » |
|
Можно увидеть код создания нитки, после получения от клиента запроса на открытие сессии. Также саму нить. Логику самого приложение можеш убрать. Меня интересует, как именно ты работаеш с сокетом только.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #8 : 03-07-2009 18:44 » |
|
Да нет проблем. Так как классы связи вынесены в отдельную DLL - то....куда выслать проект?
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #9 : 03-07-2009 18:50 » |
|
Меня проект не интересует. Да и я не смогу его запустить у себя. Код просто здесь выложи мест которые я указал.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #10 : 03-07-2009 18:53 » |
|
Хорошо. Выкладываю. Это поток, в котором сервер ждет запросы клиентов на присоединения. Вы это имели в виду? UINT Potok(LPVOID pParam) { SOCKADDR_IN saddr; WSADATA data;
// char hostname[20]; nomer_soketa=0; for(DWORD i=0;i<Max_Chislo_Klients;i++) {SocketUsed[i]=FALSE;}
if (WSAStartup(0x202,&data)) {Fl_Err=2;return 0;} if (gethostname(hostname,sizeof(hostname))) {Fl_Err=2;return 0;} start: mainSocket=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); if (mainSocket==INVALID_SOCKET) {Fl_Err=3;return 0;} saddr.sin_family=AF_INET; saddr.sin_addr.S_un.S_addr=INADDR_ANY; saddr.sin_port=htons(PortServer);
int optval = 1; int rc; rc = setsockopt(mainSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
if (bind(mainSocket,(PSOCKADDR) &saddr,sizeof(SOCKADDR_IN))) { DWORD mmm999=GetLastError(); Fl_Err=3;return 0; } Fl_Err=1; if (listen(mainSocket,Max_Chislo_Klients)) {Fl_Err=3;return 0;} if (PotokMastExit) return 0; slaveSocket[nomer_soketa]=WSAAccept(mainSocket,NULL,NULL,NULL,0); if (PotokMastExit) return 0; if (slaveSocket[nomer_soketa]==INVALID_SOCKET) {Fl_Err=3;return 0;} if (closesocket(mainSocket)) {Fl_Err=3;return 0;} if (WSAAsyncSelect(slaveSocket[nomer_soketa],MainHandle,WM_TRANSACT,FD_READ || FD_CLOSE)) {Fl_Err=3;return 0;} SocketUsed[nomer_soketa]=TRUE; Massiv_Klients[nomer_soketa].Klient_To_Server=Massiv_Klients[nomer_soketa].Server_To_Klient= Massiv_Klients[nomer_soketa].Byte_From_Klient_Soket_Service= Massiv_Klients[nomer_soketa].Byte_From_Klient=0; EventTotal[nomer_soketa]=0; EventArray[nomer_soketa][EventTotal[nomer_soketa]] = WSACreateEvent(); ZeroMemory(&AcceptOverlapped[nomer_soketa], sizeof(WSAOVERLAPPED)); AcceptOverlapped[nomer_soketa].hEvent = EventArray[nomer_soketa][EventTotal[nomer_soketa]]; DataBuf[nomer_soketa].len = DATA_BUFSIZE; DataBuf[nomer_soketa].buf = buffer[nomer_soketa]; RecvBytes[nomer_soketa]=BytesTransferred[nomer_soketa]=CallBack[nomer_soketa]= Flags[nomer_soketa]=0; EventTotal[nomer_soketa]++; DataBuf[nomer_soketa].len = DATA_BUFSIZE; DataBuf[nomer_soketa].buf = buffer[nomer_soketa]; Schitano[nomer_soketa]=true; KolvoUsedSockets++; if (nomer_soketa==Max_Chislo_Klients-1) nomer_soketa=0; else nomer_soketa++; m1: if (SocketUsed[nomer_soketa]==TRUE) { if (nomer_soketa==Max_Chislo_Klients-1) nomer_soketa=0; else nomer_soketa++; goto m1; } //PostMessage(MainHandle,WM_PAINT,0,0); m2: if (KolvoUsedSockets==Max_Chislo_Klients) { Sleep(1000); goto m2; } goto start; return 0; }
|
|
« Последнее редактирование: 03-07-2009 19:00 от Finch »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #11 : 03-07-2009 19:03 » |
|
У тебя не правильная логика серверного приложения. Попробую сейчас поправить. Предупреждаю сразу. Что могут быть ошибки в моем коде.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #12 : 03-07-2009 19:04 » |
|
Лучше на словах. Т.к. здесь почти все взято с примеров MSDN
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #13 : 03-07-2009 19:42 » |
|
DWORD WINAPI SessionThread( LPVOID lpParam ) { //lpParam у нас будет номер сокета SOCKET socket = (SOCKET) lpParam; // Тут логика работы твоего приложения с клиентом closesocket(socket); return 0; }
UINT Potok(LPVOID pParam) { SOCKADDR_IN saddr; WSADATA data; SOCKET mainSocket=INVALID_SOCKET; SOCKET slaveSocket=INVALID_SOCKET; HANDLE dwThread;
if (WSAStartup(0x202,&data)) { Fl_Err=2; return 0; } mainSocket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); if (mainSocket==INVALID_SOCKET) Fl_Err=3; else { saddr.sin_family=AF_INET; saddr.sin_addr.S_un.S_addr=INADDR_ANY; saddr.sin_port=htons(PortServer);
int optval = 1; int rc; rc = setsockopt(mainSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
if (bind(mainSocket,(PSOCKADDR) &saddr,sizeof(SOCKADDR_IN))) { DWORD mmm999=GetLastError(); Fl_Err=3; PotokMastExit = true; } Fl_Err=1; if (listen(mainSocket,Max_Chislo_Klients)) { Fl_Err=3; PotokMastExit = true; } while (!PotokMastExit) { slaveSocket = WSAAccept(mainSocket,NULL,NULL,NULL,0); if (slaveSocket != INVALID_SOCKET) { dwThread = CreateThread(NULL, 0, SessionThread, (void *)slaveSocket, 0, &dwThread); CloseHandle(dwThread); // Закрываем хендл нити, так как с серверной части он нас больше не интересует } } closesocket(mainSocket); } WSACleanup(); return 0; }
Вот так примерно должно выглядеть серверная часть приложения. Могут быть ошибки. Не компилировал и не проверял.
|
|
« Последнее редактирование: 03-07-2009 19:51 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #14 : 03-07-2009 19:44 » |
|
спасибо, попробую завтра разобраться на свежую голову.
Добавлено через 7 часов, 18 минут и 52 секунды: Попробовал. Согласен с тем, что Вы более изящно оформили код в плане обработки ошибок. Но в том то и дело, что их нет - даже когда связь валится - соединения с клиентом происходят нормально, т.е. в ветви обработки ошибок программа не попадает.
Принципиальное отличие только в том, что Вы создаете отдельный поток на каждого подключенного клиента. Для меня это не нужно да и нежелательно. Т.к. ожидание соединений вынесено в поток, то обработчик событий в сокетах я оставил в самом классе. И он один.
Так что где у меня неправильная логика - не понимаю. И потом, все это не объясняет почему возможен только 101 перезапуск (удаление и создание вновь) класса связи.
|
|
« Последнее редактирование: 04-07-2009 03:03 от Mikl »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #15 : 04-07-2009 05:49 » |
|
slaveSocket[nomer_soketa]=WSAAccept(mainSocket,NULL,NULL,NULL,0); if (PotokMastExit) return 0; if (slaveSocket[nomer_soketa]==INVALID_SOCKET) {Fl_Err=3;return 0;} if (closesocket(mainSocket)) {Fl_Err=3;return 0;}
Зачем например закрывать главный сокет? А потом его заново пересоздавать? В моем коде он создается один раз. И до тех пор пока приложение не будет закрыто. Кстати, та логика, что я привел. Это стандартная логика серверных приложений. В твоей логике. Если например клиент начнет тупить, и не отсылать запрос сразу. Сервер повиснит и не будет принимать последуюшие запросы. Т.е убить твое приложение можно просто, открыть соединение и держать его открытым. И кстати эти return 0 везде по коду, явная утечка. Текут у тебя зомби-сокеты.
|
|
« Последнее редактирование: 04-07-2009 05:51 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #16 : 04-07-2009 05:59 » |
|
Зачем например закрывать главный сокет? - Честно? А фиг его знает. Даже не задумывался. Совет дельный - сейчас переделаю, но не совсем как ты предлагаешь - просто в своем коде перенесу метку start: к коду slaveSocket[nomer_soketa]=WSAAccept(mainSocket,NULL,NULL,NULL,0); Насчет "клиент начнет тупить" - проверено неоднократно - ничего не виснет. Серверу пофиг на этого клиента. Все работает с остальными дальше. Выяснил еще один интересный факт. Сейчас усиленно гоняю приложение в режиме постоянных перезапусков класса связи и обнаружил, что ИНОГДА: 1. Перезапускаю класс связи. Ни одна из функций класса сервера НЕ возвращает ошибку. 2. netstat -n НЕ показывает что на таком-то порту что-то кем-то слушается. 3. клиент естественно говорит - сервер не найден. 4. после нескоьких(1-n) перезапусков все работает опять. По этому поводу наткнулся на http://forum.vingrad.ru/forum/topic-99522/unread-1.htmlтам описаны параметры TcpTimedWaitDelay,TcpNumConnections,MaxUserPort. У меня их в реестре нет. Может создать? Добавлено через 3 минуты и 16 секунд:Да, а насчет потоков и дескрипторов - пытаюсь понаблюдать, но толку мало - в приложении десятка 2 потоков, все постоянно "шевелится" и уловить что-то трудновато. Но устойчивого роста какого-либо показателя нет. А виртуальная память вообще подупала .
|
|
« Последнее редактирование: 04-07-2009 06:02 от Mikl »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #17 : 04-07-2009 06:06 » |
|
Я не разбирался как именно ты работаеш с клиектом при открытии сессии. Поэтому не могу ничего сказать. Но можно проверить кстати. Просто на клиенте открой соединение. И ничего не посылай серверу. А вот то, что netstat тебе показывает, что сервер ничего не слушает. Вот это есть очень плохо. Сервер должен постоянно прослушивать порт, когда приложение запушено в рабочее состояние.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #18 : 04-07-2009 06:11 » |
|
поотключал все что смог - счетчик дескрипторов подрастает. Сейчас переделаю по твоему совету, а заодно добавлю параметров в реестр, а затем снова погоняю.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #19 : 04-07-2009 06:13 » |
|
То что описано на винграде, это если ты привышаеш лимит открытых сокетов. Я не вижу в коде, сколько точно ты собираешся открывать соединений. Но явно меньше чем лимит.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #20 : 04-07-2009 06:15 » |
|
да немного штук 40 мне пока хватит, просто некоторые клиенты очень специфичны и считать их в нагрузке один к одному нельзя.
Я думал про другое - там указан максимальный порт в системе 5000. У меня в реестре таких параметров нет, а порт как раз 5001. Ну и время когда сокет в TIME_WAIT я бы сократил ниже плинтуса. Кстати вопрос до скольки? Сеть локальная, клиентов с инета нет.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #21 : 04-07-2009 06:23 » |
|
Ты не правильно понял. Это не максимальный порт. А количество открытых портов одновременно. Т.е. это количество открытых соединений одновременно. Например порт у аськи 5190. А на твоей стороне порт обязательно будет выше 32536. Кстати настоятельно не рекомендуется открывать порты на прослушивание выше чем 32536. Порты от 32536 и до 65535 предназначены для автоматической биндовки.
Кстати обрати внимание на return 0 в своем коде. Почти всегда это чревато не закрытием сокетов у тебя.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #22 : 04-07-2009 06:27 » |
|
MaxUserPort - максимально доступный открываемый № порта, по умолчанию 5000.
как раз написано порт пользователя, а не используемые (USED). Ну да это не важно и без них все работает до определенного момента.
Сокеты закрываются в деструкторе класса.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #23 : 04-07-2009 06:32 » |
|
Вот здесь тоже не правильная логика. При программировании надо почти всегда придерживаться принципа. "Где открыл, там и закрыл."
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #24 : 04-07-2009 06:34 » |
|
согласен. Туговато у меня с логикой ООП. И всякие "goto" тоже не есть гуд. Но т.к. первый язык был ассемблер - никак не могу переучиться.
Добавлено через 9 минут и 28 секунд: >Просто на клиенте открой соединение. И ничего не посылай серверу.
Попробовал. Все отлично работает - следующий спокойно цепляется и работает, а тестовый так и висит, пока сервер его сам не прибьет по другим факторам.
Добавлено через 4 часа, 30 минут и 51 секунду: to Finch
> поотключал все что смог - счетчик дескрипторов подрастает. >С ейчас переделаю по твоему совету, а заодно добавлю параметров в реестр, а затем снова погоняю.
Реестр пока решил для чистоты эксперимента не трогать. Погонял. Решились (кажется) 2 проблемы сразу - счетчик дескрипторов расти перестал. И перестала уменьшаться выделенная приложению память - раньше сползала с 600 до 300 мб.
Сейчас попробую повключать нагрузки сколько смогу, а на рабочей системе запущу в понедельник.
Спасиобо огромное.
Добавлено через 19 дней, 5 часов, 58 минут и 44 секунды: Еще раз спасибо!
Проблема решена. Сервер проработал 2 недели вывалившись по совершенно иной причине.
Вот только сомнения остались - в чем была причина?!?! Что "крамольного" в закрытии и открытии главного сокета вновь, что приводило к таким последствиям?
Но в принципе тема закрыта.
|
|
« Последнее редактирование: 23-07-2009 17:13 от Mikl »
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Offline
Пол:
Пролетал мимо
|
|
« Ответ #25 : 23-07-2009 18:58 » |
|
Ну скорее всего в подвисании ресурсов.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #26 : 27-04-2011 12:04 » |
|
Как теперь выяснилось (спустя почти 2 года) проблема не решена. Опять все повторилось. Поэтому вопрос снова открыт - какие могут быть варианты? библиотеку которая содержит классы по работе с потоками я это время не трогал. Менял только код основного приложения. Все работало. Сейчас переехал на другое железо - вылезла старая проблема. Дистрибутивы ОС и VS один и тот же.
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #27 : 27-04-2011 16:35 » |
|
Делай проверки, лови исключения. Отладить по фотографии нельзя.
Серверный сокет вполне можно закрыть. Открывая его вновь не забудь REUSE.
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Mikl
Постоялец
Offline
|
|
« Ответ #28 : 27-04-2011 16:40 » |
|
Про фотографии я в курсе . Пока рою землю. Дело в том что сервер работает под отладчиком - и никаких проблем не возникает - студия не ругается ни на что. REUSE - поподробнее можно? У меня при детекте потери связи класс отвечающий за связь уничтожается и создается заново. Единственное что до меня дошло - надо попробовать делать WSACleanup и затем WSAStartup. Но этот вариант означает - есть грабли в библиотеке WSA. В подтверждение этого http://muforum.info/forums/index.php?showtopic=1586Не кто не сталкивался с заплатками от MS для WinSocket?
|
|
|
Записан
|
|
|
|
RXL
|
|
« Ответ #29 : 27-04-2011 17:44 » |
|
Опция сокета - port reuse. Чтобы система не отвергала попытку bind на сокет, пока еще существуют соединения на этот порт (активные или в финишных стадиях).
|
|
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
|