Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 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
4-1164296258
Вольный Стрелок
2006-11-23 18:37
2007.04.22
CreateProcess консольной программы


2-1175325231
Roman_S
2007-03-31 11:13
2007.04.22
Как передеть файл с компа1 на комп2 (и обратно) через Net?


3-1170747668
RomanH
2007-02-06 10:41
2007.04.22
SQL-запрос


11-1154888270
Александра
2006-08-06 22:17
2007.04.22
OnFocus для EditBox


15-1174588113
JohnKorsh
2007-03-22 21:28
2007.04.22
Как из файла *.msg извлечь приложение?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский