Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.10.31;
Скачать: CL | DM;

Вниз

Проблемы с 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;
Скачать: CL | DM;

Наверх




Память: 0.52 MB
Время: 0.029 c
6-1093338639
Дмитрий Ботвин
2004-08-24 13:10
2004.10.31
Получение списка файлов с ftp-сервера


8-1091407249
widoms
2004-08-02 04:40
2004.10.31
Компоненты для работы с изображениями.


1-1097676709
AlexanderSK
2004-10-13 18:11
2004.10.31
TTreeView принудительное обновление TTreeNode.


1-1097828947
STM
2004-10-15 12:29
2004.10.31
Application.Minimize


3-1096741524
Донской
2004-10-02 22:25
2004.10.31
deleterecords(arall) - "операция недопустима в данном контексте"