Форум: "Сети";
Текущий архив: 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.037 c