Форум: "Начинающим";
Текущий архив: 2007.04.22;
Скачать: [xml.tar.bz2];
ВнизОпределение разрыва связи с Pipe - клиентом. Найти похожие ветки
← →
Riply © (2007-03-27 14:53) [0]Здравствуйте !
Сейчас пошлют меня на семнадцатую строчку :) Но рискну.
Весь код привести нереально. Куски выдергивались и "на лету"
менялись в попытке сделать их интуитивно понятными.
Сто лет назад написала NamedPipe - сервер.
И служил он мне верой и правдой все это время.
Но решила я его переписать, т.к. посчитала,
что на каждого клиента отдельная нить - ну уж очень расточительно.
Он обрабатывал запросы от клиентов, примерно следующим образом:function TNpBase.ReadPipe(const hPipe: THandle; const Ind: DWord; pOverLap: POVERLAPPED; pEventArr: PHandleArray; pBuf: PChar): integer;
var
BytesRead: Dword;
begin
Result := -1;
if not ReadFile(hPipe, pBuf^, FBufSize, BytesRead, pOverLap) then
if not Wait_IO_PENDIMG(pEventArr, Ind, GetLastError, INFINITE) then Exit;
if GetOverlappedResult(hPipe, pOverLap^, BytesRead, False) then Result := BytesRead;
function TNpBase.Wait_IO_PENDIMG(pEventArr: PHandleArray; const Index, LastErr: DWord; const TmOut: DWord = INFINITE):Boolean;
var
EventCount: DWord;
begin
Result:=False;
case LastErr of
ERROR_IO_PENDING:
begin
if FcbMess = NP_CLIENT then EventCount:=ClientEventCount else
if FcbMess = NP_SERVER then EventCount:=ServerEventCount else EventCount:=1;
case WaitForMultipleObjects(EventCount, pEventArr, False, TmOut) of
WAIT_OBJECT_0: Result:=True;
/////////////////////
WAIT_TIMEOUT: if Command <> ALL_HANDS_ON_DECK then Call_Back(NP_ERROR, WAIT_TIMEOUT, Index, LPARAM(PChar("WAIT_TIMEOUT")));
else Call_Back(NP_ERROR, GetLastError, Index, LPARAM(PChar("ERROR_IO_PENDING")));
end;
end;
else Call_Back(NP_ERROR, LastErr, Index, LPARAM(PChar("ERROR_IO_PENDING")));
end;
end;
Первый элемент в pEventArr - Handle из OVERLAPPED структуры.
Все работало как надо. При закрытии Pipe на другой стороне, на нашей сразу срабатывал
Wait_IO_PENDIMG с результатом True, GetOverlappedResult выдавал False и GetLastErorr - ERROR_BROKEN_PIPE.
Т.е. я тут же узнавала о разрыве связи.
Пыталась делать аналогично.
При попытке использовать BindIoCompletionCallback - все хорошо, но при "закрытии Pipe на другой стороне",
она не вызывает CallBack процедуру и мы продолжаем ждать сообщений от уже мертвого клиента :(
И это логично, зачем вызывать, если не было завершенной операции.
Попробовала использовать RegisterWaitForSingleObject с, примерно, такой CallBack процедурой:procedure ReadPipeCallBack(pPOverLap: Pointer; TimerOrWaitFired: Boolean); stdcall;
var
BytesRead: Dword;
LastErr: integer;
begin
with PPipeOverLap(pPOverLap)^ do
begin
UnregisterWaitEx(hWait, 0);
if GetOverlappedResult(hPipe, OverLap, BytesRead, False) then
begin
// Обрабатываем сообщение
while ReadFile(hPipe, Buffer[0], Length(Buffer), BytesRead, @OverLap) do
begin
// Обрабатываем сообщение
end;
LastErr := GetLastError;
if LastErr = ERROR_IO_PENDING
then
if RegisterWaitForSingleObject(hWait, OverLap.hEvent, ReadPipeCallBack,
pPOverLap, INFINITE, WT_EXECUTEONLYONCE)
then Exit
else //////////////////////
else /////////
end
else // Пытаемся поймать момент "отсоединения клиента"
end;
end;
(Все: и логика обработки, и способы(параметры) открытия и закрытия Pipe-ов, и т.д., как и в старом сервере).
И снова старая проблема: При "закрытии Pipe на другой стороне", ReadPipeCallBack не вызывается.
Хотя, казалось бы должна, раз внутри используется WaitForMultipleObjects, как и у нас в старом ватианте сервера.
Уже который день бьюсь над этим. Подскажите, пожалуйста, как "поймать разрыв связи" ?
Или, может, идеология с которой я пытаюсь все организовать, не верна в корне ?
P.S. Пинговать клиентов считаю не достойным решением :)
← →
Сергей М. © (2007-03-27 16:15) [1]
> на каждого клиента отдельная нить - ну уж очень расточительно
Ну это как посмотреть)
Если требуется мгновенная реакция сервера на запрос любого клиента в любое время, и число одновременно активных клиентов разумно ограничено и управляемо, то отдельная нить - лучшее решение. Др.вопрос, что "аппетиты" нити в части ресурсов нужно регулировать, хотя бы организуя ее стек минимально необходимого размера.
С др.стороны, компромиссным решением возможно будет организация пула нитей фиксированного или настраиваемого размера. Например, пул в 10 нитей на каждые 100 активных клиентов.
← →
Сергей М. © (2007-03-27 16:20) [2]Кстати, о каком разрыве идет речь - о программном (клиент по своей инициативе закрывает пайп) или об аварийном (физический разрыв сетевого соединения клиента не по его инициативе) ?
← →
Riply © (2007-03-27 17:04) [3]> [1] Сергей М. © (27.03.07 16:15)
>Если требуется мгновенная реакция сервера на запрос
>любого клиента в любое время, и число одновременно
>активных клиентов разумно ограничено и управляемо,
>то отдельная нить - лучшее решение.
Я очень жадная :)
Мне не нравиться, что n нитей висят и жудут, каждая от своего клиента,
когда тот соблаговолит послать запрос.
Пусть это знаменательное событие (запрос от любого клиента) ожидает одна нить :)
>С др.стороны, компромиссным решением возможно будет организация
>пула нитей фиксированного или настраиваемого размера.
Я уже склоняюсь к мысли попробовать написать собственный пул,
но меня останавливают следующее:
1. Полное отсутствие опыта.
2. Функциональность моего супер-пупер пула, я представляю себе,
практически, полностью аналогичной RegisterWaitForSingleObject.
3. Очевидность того, что если я и добавлю что-то нужное,
то вместе с этим будет добавлена еще и куча ошибок.
И последнее: это - изобретение велосипеда.
Скорее всего, я просто неправильно использую RegisterWaitForSingleObject. :(
и, то чего мне не хватает, уже есть в нем.
← →
Riply © (2007-03-27 17:08) [4]>[2] Сергей М. © (27.03.07 16:20)
>о каком разрыве идет речь - о программном (клиент по своей инициативе закрывает пайп) или об аварийном >(физический разрыв сетевого соединения клиента не по его инициативе) ?
Мой старый сервер корректно фиксировал и когда "клиент по своей инициативе закрывает пайп" и
когда я прибивала клиента из TaskMng-а. И это мне очень нравилось.
Выдергивать сетевой шнур не пыталась :)
← →
Сергей М. © (2007-03-28 09:44) [5]
> Riply © (27.03.07 17:04) [3]
> Мне не нравиться, что n нитей висят и жудут, каждая от своего
> клиента,
> когда тот соблаговолит послать запрос
Ну что значит "нравится" или "не нравится" ?
У тебя ж, наверно, есть некие конкретные требования по предельному времени реакции сервера на запросы клиентов ? Вот от этих требований и следует исходить при принятии решения о мультипоточности сервера ..
> Пусть это знаменательное событие (запрос от любого клиента)
> ожидает одна нить
Да на здоровье ! Пусть один поток (транспортный) инициирует трансп.операции (прием, передача) и он же обслуживает трансп.события (успешное или неуспешное завершение приема, передачи) для всех клиентских экз-ров пайпов, а ряд других потоков (исполняющих) в составе пула обрабатывают принятые данные и формируют результаты.
> 1. Полное отсутствие опыта
Не так страшен черт, как его малютки)
В конце-концов можно подглядеть, как это реализовано, к примеру, в TServerSocket - в режиме stThreadBlocking там как раз неализован простейший кэш потоков (в нек.натяжкой кэш можно ассоциировать с тред-пулом). Ну или в классе TIdThreadMgr[Pool] составе Indy - там реализован полноценный тред-пул.
> Скорее всего, я просто неправильно использую RegisterWaitForSingleObject
А зачем она тебе вообще понадобилась ? Поясни ..
И уж если речь зашла о колбэках, почему не использовать Read/WriteFileEx ?
← →
Riply © (2007-03-28 14:59) [6]>[5] Сергей М. © (28.03.07 09:44)
>Ну что значит "нравится" или "не нравится" ?
>У тебя ж, наверно, есть некие конкретные требования
"конкретные требования" разумеется есть.
Но так хочется решить задачу в максимально общем виде,
чтобы при (не совсем уж кардинальном) изменении "конкретных требований",
не пришлось пересматривать саму логику построения. :)
Хотя, я понимаю, что (в большинстве случаев) это нереально :(
>* Пусть один поток (транспортный) инициирует трансп.операции (прием, передача) и он же обслуживает
>* трансп.события (успешное или неуспешное завершение приема, передачи) для всех клиентских экз-ров пайпов,
>* а ряд других потоков (исполняющих) в составе пула обрабатывают принятые данные и формируют результаты.
Именно так я и вижу себе стуктуру будущего сервера.
>В конце-концов можно подглядеть, как это реализовано, к примеру, в TServerSocket
>Ну или в классе TIdThreadMgr[Pool] составе Indy - там реализован полноценный тред-пул.
Большое спасибо. Я как-то уже говорила, что из меня никудышный сыщик:)
>А зачем она тебе вообще понадобилась ? Поясни ..
Если я правильно понимаю, то в RegisterWaitForSingleObject реализована не только возможность
ожидания более чем MAXIMUM_WAIT_OBJECTS объектов, но еще и организация пула потоков для CallBack-ов.
А это, практически то, о чем ты писал(отмечено звездочками).
Т.е., если уметь с ней грамотно обращаться, то она может взять на себя
организацию того, что нам нужно, а нам останется только наслаждаться результатами ее(своей) работы :)
← →
Сергей М. © (2007-03-28 15:29) [7]
> хочется решить задачу в максимально общем виде
Тогда тебя спасет один транспортный поток на все пайпы и в нем Read/WriteFileEx + MsgWaitForMultipleObjectEx
> организация пула потоков для CallBack-ов
Это мухи+котлеты)
← →
Riply © (2007-03-29 00:38) [8]Говорят, что первая заповедь рыболова: "Ловить рыбу там, где она есть".
Основываясь на этой заповеди, совершила очередное великое открытие :)
Прежде чем кричать на весь мир, что не ловиться событие
разрыва связи с Pipe - клиентом, очень желательно убедится,
что клиент не забыл закрыть пайповый Handle. :))
← →
Заинтересованный (2007-03-29 07:12) [9]>Riply © (29.03.07 00:38) [8]
>очень желательно убедится, что клиент не забыл закрыть пайповый Handle
А если дядя Вася топором по кабелю? Или тетя Клава шваброй по Reset?
← →
Сергей М. © (2007-03-29 08:16) [10]
> Заинтересованный (29.03.07 07:12) [9]
> А если дядя Вася топором по кабелю? Или тетя Клава шваброй
> по Reset?
А вот при таком веселом раскладе многое будет зависеть от того, какие сетевые программно-аппаратные устройства работают на узлах маршрутов доставки инф.пакетов между сервером и клиентом (хабы, роутеры, мосты и т.д. и т.п.)
← →
Riply © (2007-03-29 15:45) [11]> [9] Заинтересованный (29.03.07 07:12)
>А если дядя Вася топором по кабелю?
В таких условиях не тестировала, на при "варварском" уничтожении клиента - отлавливается :)
← →
Сергей М. © (2007-03-29 16:04) [12]
> Riply © (29.03.07 15:45) [11]
> на при "варварском" уничтожении клиента
Твоя ошибка в том, что партнеров по УЖЕ установленному соединению ты делишь на "клиента" и "сервера".
После установки соединения нет ни "клиента" ни "сервера" - есть партнеры по конкретному соединению.
← →
Riply © (2007-03-29 16:56) [13]> [12] Сергей М. © (29.03.07 16:04)
>После установки соединения нет ни "клиента" ни "сервера" - есть партнеры по конкретному соединению.
Я это понимаю. Просто так удобнее "обзывать" их.
Правда разница в их поведении есть. Клиент(у меня) при установлении связи,
первым делом запрашивает параметры Pipe"а (например, размеры In/Out Buffer"ов) и подстраивается под сервер.
← →
Riply © (2007-03-29 23:30) [14]Чтобы не создавать новую ветку, спрошу в этой.
Пытаюсь отслеживать неоднократное "срабатывание" большого кол-ва объектов.
Можно поступить так:
Вызываем ее с флагом (не WT_EXECUTEINWAITTHREAD т.к. CallBack может занять некоторое время),
и с CallBack, примерно такого вида:procedure WaitObjectCallBack(pParam: Pointer; TimerOrWaitFired: Boolean); stdcall;
begin
with PWaitThreadParam(pParam)^ do // WaitThreadParam - нами определенная стуктура.
try
ResetEvent(hEvent); // Здесь hEvent - Handle нашего события, которого ждали.
Этот вариант плох тем, что WaitObjectCallBack может быть вызвана 138 раз,
прежде чем мы успеем вызвать ResetEvent. :)
Вариант второй:
Вызываем ее с флагом WT_EXECUTEONLYONCE.
Здесь все работает, но есть очень большое "но".
После каждого удачного вызова WaitObjectCallBack, мы должны
снимать регистрацию нашего события ( UnregisterWait ) и заново его регистрировать,
что, как мне кажется, "не есть хорошо и правильно" :)
Как бы исхитриться и использовать первый вариант,
но с одним вызовом WaitObjectCallBack для каждого срабатывания нашего события ?
← →
Заинтересованный (2007-03-30 08:33) [15]>Riply ©
>В таких условиях не тестировала, на при "варварском" уничтожении клиента - отлавливается :)
Через TerminateProcess? Это не интересно:(
>ResetEvent(hEvent)
А ручной сброс использовать обязательно? Может ManualReset в False поставить?
← →
Сергей М. © (2007-03-30 09:33) [16]
> Riply
Я так и не понял, чем тебя MsgWaitForMultipleObjectEx+ReadFileEx+WriteFileEx не устраивает ?
← →
Riply © (2007-03-30 17:51) [17]>[15] Заинтересованный (30.03.07 08:33)
>А ручной сброс использовать обязательно? Может ManualReset в False поставить?
Да. В рамках формулировки задачи в [14] Riply © (29.03.07 23:30),
AutoReset может оказаться панацеей.
>[16] Сергей М. © (30.03.07 09:33)
>Я так и не понял, чем тебя MsgWaitForMultipleObjectEx+ReadFileEx+WriteFileEx не устраивает ?
Рогом уперлась: хочу использовать RegisterWaitForSingleObject и все тут !
Вот рога пообломаю, тогда вернусь к MsgWaitForMultipleObjectEx :))
А если серьезно, то (если конесно я все правильно понимаю)
в MsgWaitForMultipleObjectEx, все равно, придется обходить ограничение MAXIMUM_WAIT_OBJECTS.
И снова наши взоры устремяться к RegisterWaitForSingleObject, либо созданию "ждущего" пула вручную.
Страницы: 1 вся ветка
Форум: "Начинающим";
Текущий архив: 2007.04.22;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.039 c