информационная безопасность
без паники и всерьез
 подробно о проектеRambler's Top100
Все любят медАтака на InternetПортрет посетителя
BugTraq.Ru
Русский BugTraq
 Анализ криптографических сетевых... 
 Модель надежности двухузлового... 
 Специальные марковские модели надежности... 
 Три миллиона электронных замков... 
 Doom на газонокосилках 
 Умер Никлаус Вирт 
главная обзор RSN блог библиотека закон бред форум dnet о проекте
bugtraq.ru / форум / programming
Имя Пароль
ФОРУМ
все доски
FAQ
IRC
новые сообщения
site updates
guestbook
beginners
sysadmin
programming
operating systems
theory
web building
software
hardware
networking
law
hacking
gadgets
job
dnet
humor
miscellaneous
scrap
регистрация





Легенда:
  новое сообщение
  закрытая нитка
  новое сообщение
  в закрытой нитке
  старое сообщение
  • Напоминаю, что масса вопросов по функционированию форума снимается после прочтения его описания.
  • Новичкам также крайне полезно ознакомиться с данным документом.
В принципе, вопрос был даже не в том , как построить... 23.08.07 07:56  Число просмотров: 3152
Автор: void <Grebnev Valery> Статус: Elderman
<"чистая" ссылка>
В принципе, вопрос был даже не в том , как построить коммуникации Client/Server более надёжными, а как сделать сервер менее чувствительным к ошибкам в дизайне клиента и коммуникациям.

> Я никогда не работал с пайпами через IOCP, но, например, с
> сокетами такого:
> > Если клиент прочитает только часть из
> _tcslen(m_buffer)+1,
> > то GetQueuedCompletionStatus(hIOCP, &dwNumBytes,
> > &CompKey, &pOverlapped, PIPE_TIMEOUT)не вернёт
> > соответствующий риквест на серверной стороне.
> быть не может.

Ну может такое поведение IOCP выглядит вполне логично для message-ориентированного Client(PIPE_READMODE_MESSAGE)/Server(PIPE_TYPE_MESSAGE) обмена?

>Да и вообще какое-то странное поведение, когда GetQueuedCompletionStatus возвращается по >_получению_данных_клиентом_.

Я этого не говорил. Если клиент неправильно вычитывает из PIPE (например, не все данные), то операция WriteFile на серверной стороне помечается в очереди как pending и IOCP не устанавливает такую операцию завершённой (completion).

Некоторые смутные пояснения можно найти здесь(хотя я могу ошибаться):
http://msdn2.microsoft.com/en-us/library/aa365747.aspx

“...If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the write operation starts at the offset specified in the OVERLAPPED structure and WriteFile may return before the write operation has been completed. In this case, WriteFile returns FALSE and the GetLastError function returns ERROR_IO_PENDING... The event specified in the OVERLAPPED structure is set to the signaled state upon completion of the write operation....”

После асинхронного вызова WriteFile, который затем оказывается в состоянии ERROR_IO_PENDING, GetQueuedCompletionStatus не вернётся, вернее ждать здесь нечего до I/O completion (или ошибки на IOCP):

“... WriteFile resets the event specified by the hEvent member of the OVERLAPPED structure to a nonsignaled state when it begins the I/O operation...”

Рассмотрим нехитрый дизайн (ниже опущена обработка ошибок и прочая обвязка):
Обмен – message ориентированный. Серверный PIPE создаётся, как

HANDLE hPipe = ::CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, PIPE_BUF_SIZE, PIPE_BUF_SIZE, PIPE_TIMEOUT, NULL);

Клиент создаётся как:

hNamedPipe = CreateFile( PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
<skip>
DWORD dwMode = PIPE_READMODE_MESSAGE; SetNamedPipeHandleState( hNamedPipe, &dwMode, NULL, NULL );


Пусть для обмена на сервере используется простая per client структура:

struct OVERLAPPED_PERCLIENT: public OVERLAPPED
{
public:
enum enCompleted { read, write, unknown };

private:
TCHAR m_buffer[IO_BUFSIZE];
HANDLE m_device;
enCompleted m_status;

public:
...
<skip>
...
inline enCompleted GetStatus( void ) { return m_status; }
inline HANDLE GetDevice( void ) {return (m_device); }
bool ReadFile( void ) {
m_status = OVERLAPPED_PERCLIENT::read;
return(TRUE ==::ReadFile( m_device, m_buffer, (DWORD) array_size(m_buffer), NULL, this));}

bool WriteFile( void ) {
m_status = OVERLAPPED_PERCLIENT::write;
m_buffer[array_size(m_buffer)-1] = _T('\0');
return(TRUE==::WriteFile(m_device, m_buffer, (DWORD) _tcslen(m_buffer)+1, NULL, this)); }

};


Пусть упрощённая реализация рабочих потоков:

DWORD WINAPI pipeWorkerThread(LPVOID lParam)
{
DWORD rc = 1;
HANDLE hIOCP = (HANDLE)lParam;
DWORD dwNumBytes = 0;
ULONG_PTR CompKey = 0;
OVERLAPPED* pOverlapped = NULL;


while ( true )
{
BOOL fOk = GetQueuedCompletionStatus(hIOCP, &dwNumBytes, &CompKey, &pOverlapped, PIPE_TIMEOUT);
OVERLAPPED_PERCLIENT * pClient = (OVERLAPPED_PERCLIENT *) pOverlapped;

if (fOk) {

OVERLAPPED_PERCLIENT::enCompleted completed = pClient->GetStatus();
if ( CK_REQUEST == CompKey )
{
if ( completed == OVERLAPPED_PERCLIENT::read )
{
_tprintf(_T("Read from a client-> %s\n"), pClient->GetBuffer() );
<...>
<skip>
<...>

pClient->SetReplyBuffer();
pClient->WriteFile();
}
else if ( completed == OVERLAPPED_PERCLIENT::write )
{
pClient->ReadFile();
}
else if ( .... <skip>
{
<...>
<skip>
<...>
}
continue;
}
} else {
DWORD dwError = GetLastError();
<...>
<skip ERROR_BROKEN_PIPE, WAIT_TIMEOUT, ERROR_OPERATION_ABORTED, ....>
<...>
}

}
return rc;
}


Пусть теперь, сервер, прочитав сообщение клиента, отправляет клиенту ответ:

if ( completed == OVERLAPPED_PERCLIENT::read )
{
_tprintf(_T("Read from a client-> %s\n"), pClient->GetBuffer() );
<...>
<skip>
<...>

pClient->SetReplyBuffer();
pClient->WriteFile();
}



Если клиент, например, не вычитывает весь посланный ему message, то GetQueuedCompletionStatus на сервере не вернётся. Даже более того, если клиент теперь будет слать сообщения серверу, то сервер о них не узнает, т.к. он слушает по завершению completion WriteFile:

else if ( completed == OVERLAPPED_PERCLIENT::write )
{
pClient->ReadFile();
}


Можно немного улучшить ситуацию, если на сервере при посылке сообщения анализировать:

if ( ! pClient->WriteFile() ) {
DWORD dwError = GetLastError();
if ( dwError == ERROR_INVALID_USER_BUFFER|dwError == ERROR_NOT_ENOUGH_MEMORY)
{
...
} else if (dwError == ERROR_IO_PENDING ) {
...
}
else {
}

}

Самое простое, что можно сделать, так это сразу же прибить клиента с dwError == ERROR_IO_PENDING. Но это уж больно прямолинейно, поскольку ошибка может быть связана и с объективными причинами на клиенте или в коммуникациях. Что со всем этим лучше делать – не знаю.
<programming> Поиск 






Rambler's Top100
Рейтинг@Mail.ru


  Copyright © 2001-2024 Dmitry Leonov   Page build time: 0 s   Design: Vadim Derkach