Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.10.31;
Скачать: [xml.tar.bz2];

Вниз

Проблемы с WSAEventSelect и Accept   Найти похожие ветки 

 
atruhin ©   (2004-08-20 09:54) [0]

Многопоточный сервер. Поток ожидает запросов на подключение и акцептирует сокет. Если Accept выполняется в блокирующем режиме т.е. так ка закоментированно - все нормально. Если убрать комментарий несколько коннектов может пройти, а потом сокет перестает реагировать на запрос. Т.е. код остонавливается на
WaitForMultipleObjects

procedure TAcceptThread.Execute;
 var
   FAddr: TSockAddrIn;
   Len: Integer;
   IntPort : PInternalPort;
   Hs : array [0..3] of THandle;
   WaitResult : DWord;
 begin
   FListenSocket := socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
   if FListenSocket = INVALID_SOCKET then raise TMyExcept.Create(erNoCreateServer);
   FAddr.sin_family := PF_INET;
   FAddr.sin_addr.s_addr := INADDR_ANY;
   FAddr.sin_port := htons(FListenPort);
   bind(FListenSocket, @FAddr, SizeOf(FAddr));
   listen(FListenSocket, 100);
   Hs[0] := WSA_INVALID_EVENT;
   Hs[0] := WSACreateEvent;
//    if WSAEventSelect(FListenSocket, Hs[0], FD_ACCEPT) = SOCKET_ERROR
//                        then raise TMyExcept.Create(erNotListen);
   Hs[1] := GV^.EndWorkEvent;
   try
     while not Terminated do begin
//        WaitResult := WaitForMultipleObjects(2, @Hs[0], false, INFINITE);
//         case WaitResult of
//            WAIT_FAILED: raise TMyExcept.Create(erAny); // неправильный вызов функции (неверный описатель?)
//            WAIT_OBJECT_0 + 0 : begin // Подключился новый клиент
              New(IntPort);
              Len:=SizeOf(TSockAddr);
              IntPort^.Port:=Accept(FListenSocket,FAddr,Len);
              if IntPort^.Port <> INVALID_SOCKET then begin
                 IntPort^.Time := Now;
                   EnterCriticalSection(GV^.CrsPortList);
                   try
                     GV^.IntPorts.Add(IntPort);
                   except
                   end;
                   LeaveCriticalSection(GV^.CrsPortList);
                   ReleaseSemaphore(GV^.NextThread, 1, nil);
              end else
              Dispose(IntPort);
//              WSAResetEvent(Hs[0]);
//            end;
//            WAIT_OBJECT_0 + 1 : break; // Завершение работы
//          end;
     end;
   finally
     {$ifdef debug} Writeln("Close listen socket"); {$endif}
     shutdown(FListenSocket,2);
     closesocket(FListenSocket);
   end;
end;


 
atruhin ©   (2004-08-20 10:00) [1]

При копировании ошибся. Строки идут в таком порядке
  Hs[1] := GV^.EndWorkEvent;
  try
    while not Terminated do begin
//    if WSAEventSelect(FListenSocket, Hs[0], FD_ACCEPT) = SOCKET_ERROR
//                        then raise TMyExcept.Create(erNotListen);
//        WaitResult := WaitForMultipleObjects(2, @Hs[0], false, INFINITE);


 
Polevi ©   (2004-08-20 10:07) [2]

WSACreateEvent

The event object created by this function is manual reset, with an initial state of nonsignaled


 
Polevi ©   (2004-08-20 10:08) [3]

хотя сорри, есть ResetEvent


 
Digitman ©   (2004-08-20 10:26) [4]


> atruhin


почему бы WSAResetEvent(Hs[0]) не сделать самым первым (и безусловно выполняемым) оператором в WAIT_OBJECT_0 ?


 
atruhin ©   (2004-08-20 10:48) [5]

Вообще WaitForMultipleObject я использую для реакции на событие завершения всех процессов в пуле. Если использовать select приходиться делать достаточно малый таймаут и проверять событие, показалось некрасиво.
Может есть другие вариаты ?
Кстати я слышал в indi на select сделано. К сожалению нет под рукой исходников. Как там завершение потока отлавливается.


 
Digitman ©   (2004-08-20 10:56) [6]


> atruhin ©   (20.08.04 10:48) [5]


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

если трэд занят только акцептированием (и более ничем), то в принципе и неблок.режим акцептирования (который ты пытаешься здесь задействовать) вовсе не нужен : просто вызывай блокирующий accept() в цикле - и всех делов ... а когда нужно будет прервать цикл, "висящий" в этот момент на выполнении блокирующего accept(), просто выполни из другого трэда closesocket(слушающее_гнездо), при этом accept() немедленно вернет управление с соотв.кодом ошибки, что и будет признаком выхода из цикла с последующим безусловным завершением поточной ф-ции


 
atruhin ©   (2004-08-20 11:15) [7]

>Digitman
Да это я знаю, но в моем варианте есть событие которое каждый поток отслеживает и сам завершается. Т.е. потоки обработки, акцептирования, поток отслеживания размера пула и т.д. работают аналогично. А чтобы >выполни из другого трэда closesocket(слушающее_гнездо), придется обрабатывать отдельно, лезть в данные другого потока (объекта), просто не хотелось.
Кстати совет
>почему бы WSAResetEvent(Hs[0]) не сделать самым первым
вроде помогает, сейчас тестирую, пока сбоев нет, но нужно время.
Тогда Вопрос, а что ещё могла вернуть


 
Digitman ©   (2004-08-20 11:18) [8]


> atruhin


говоря вообще, я бы посоветовал не "грузить" акцептирующий трэд несвойственной ему логикой, как то создание-уничтожение слушающего гнезда и работа с пулами

слушающее гнездо создает-уничтожает некий "главный" объект, представляющий интерфейс сервера в целом

после успешного создания слушающего гнезда, его бИнда и перевода в режим прослушивания этот объект создает и стартует акцептирующий трэд, передавая ему параметрами хэндл гнезда и адрес процедуры обработки события успешного акцепта.. когда сервер должен быть деактивирован, объект уничтожает слушающее гнездо и разрушает акцептирующий тред

как только акцептирующий тред выполнил очередной успешный акцепт, от возбуждает событи OnAccept, передавая в его обработчик хэндл вновь созданного гнезда, а объект, обрабатывающий это событие, волен уже делать с этим гнездом все что угодно - слушающему треду не должно быть до этого никакого дела, это не его забота

кр.того, коль речь уж зашла о написании собственного серв.класса, я бы посоветовал задействовать и режим условного акцепта для W2k/XP (см. Winsock2.WSAAccept) - фича отнюдь не лишняя для серверов, реализующих "отлуп" нежелательных соединений


 
Digitman ©   (2004-08-20 11:22) [9]


> вроде помогает


вот отсюда можно уже сделать кой-каие выводы : в твоем варианте до этой строки в рез-те каких-то ошибочных ситуаций дело не доходило в какой-то момент


 
atruhin ©   (2004-08-20 11:49) [10]

>Digitman
Спасибо за советы.
У меня слушающее гнездо создавалось в акцептирующем треде. Возможно ваш вариант красивее. Подумаю. ;)


 
Digitman ©   (2004-08-20 11:54) [11]


> atruhin ©   (20.08.04 11:49) [10]


> У меня слушающее гнездо создавалось в акцептирующем треде.
> Возможно ваш вариант красивее


а ты велосипеды-то не изобретай без особенной надобности !
посмотри как все это у Борланда разрулено в scktcomp и сделай лучше, но общую идею не игнорируй - она не так уж и плоха у него)


 
Verg ©   (2004-08-20 12:54) [12]

Для данного случая я бы не советовал вообще пользоваться WSAResetEvent. Просто, пока до этого вызова код дойдет никто не грарнтировал, что в очереди установленных соединений слушающего сокета не появилось еще одно новое. А ты бах и сбрасываешь событие, которое уже никогда не взведется, т.к. взводится оно по факту появления в пустой очереди нового соединения. Это может быть причиной...

int TIPServer::process_events(void) {
 SOCKET new_client_socket;
 TSockAddress* new_client_address;
 TServerClient* clt;
 WSANETWORKEVENTS evs;
 int i;
 if( WSAEnumNetworkEvents(serv_sock, wsa_event, &evs) == SOCKET_ERROR )
   return 0;
 for( i = 0; i < FD_MAX_EVENTS; i++) {
   if( evs.lNetworkEvents & (1 << i) ) {
     if( evs.iErrorCode[i] )
       return 0;
     else
       switch( 1 << i ) {
         case FD_ACCEPT:
           new_client_address = (new TSockAddress())->make(sa->addr->sa_family, NULL, sa->adatalen);

           new_client_socket = accept(serv_sock, new_client_address->addr,
                                      (int*)&new_client_address->addrlen);

           if(new_client_socket == INVALID_SOCKET) {
             delete new_client_address;
             return 0;
           }
           clt = (TServerClient*)Unused.head;
           if( ! clt )
             clt = create_client();
           else
             Unused.del(clt);
           Clients.add(clt);
           clt->new_socket(new_client_socket, new_client_address);
           break;
       }
   }
 }
 return 1;
}

DWORD TIPServer::execute() {
 HANDLE evts[2];

 evts[0] = exitevent;
 evts[1] = wsa_event;
 if( sa == NULL )
   return 0;
 if( serv_sock == INVALID_SOCKET)
   return 0;
 if( bind(serv_sock, sa->addr, sa->addrlen) == SOCKET_ERROR )
   return 0;
 if( WSAEventSelect(serv_sock, wsa_event, FD_ACCEPT) == SOCKET_ERROR )
   return 0;
 if( listen(serv_sock, 5) == SOCKET_ERROR )
   return 0;
 while(1)
   switch( WaitForMultipleObjects(2, evts, FALSE, INFINITE ) ) {
     case WAIT_OBJECT_0 + 1:
       if( process_events() )
         break;
     case WAIT_OBJECT_0:
       disconnect(INFINITE);
       return 0;
   }
 return 0;
}


 
Digitman ©   (2004-08-20 13:22) [13]

в продолжение справедливого замечания Verg ©   (20.08.04 12:54) [12] можно исправить эту досадную оплошность и таким еще способом :

wait_object_0:
begin
 WSAResetEvent(...);
 while True do
   begin
     hSocket := accept(...);
     if hSocket <> SOCKET_ERROR then
       DoOnAccept(hSocket) //успешный акцепт очередного запроса
     else
       case WSAGetLastError of
        WSAEWOULDBLOCK, WSAENOBUFS: break;
       else
         raise ....//генерация исключения для прерывания внешнего цикла и завершения поточной ф-ции
       end;
       
   end;
end;


 
atruhin ©   (2004-08-20 14:32) [14]

Вот теперь точно спасибо за ответы. Как видимо у меня и была ошибка описанная Verg-ом, т.к. подвисания происходили в произвольный момент при интенсивной нагрузке.
Считал что событие взводится при каждом запросе соединения. :(



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2004.10.31;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.5 MB
Время: 0.038 c
14-1097703650
quickblack
2004-10-14 01:40
2004.10.31
Востановление rar


6-1093041116
Baron Fon FasHisT
2004-08-21 02:31
2004.10.31
Заход на страницу.


4-1092918672
menart
2004-08-19 16:31
2004.10.31
Как можно остановить службу на какое-то и потом ее запустить?


3-1096530936
aleks-ran
2004-09-30 11:55
2004.10.31
Транзакция и INSERT? D6, Paradox


1-1097755008
denis24
2004-10-14 15:56
2004.10.31
цвет курсора в гриде





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский