Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Использование io completion ports.  (Прочитано 12336 раз)
0 Пользователей и 5 Гостей смотрят эту тему.
Tiger62-vip
Гость
« : 13-07-2006 03:23 » 

Я пытаюсь написать сервер на модели ввода-вывода построеной на iocp, и у меня даже кое-что получается, но все никак не могу отловить бесконечные глюки. Проблема в том, что я никак не могу найти информацию о некоторых особенностях поведения сокетов в этой модели ввода-вывода, в связи с чем возникли следующие вопросы:
1) Может ли при использовании WSASend приходить несколько пакетов завершения?
2) Если WSARecv или WSASend возвращают SOCKET_ERROR и при этом WSAGetLastError() != WSA_IO_PENDING, то можно ли быть увереным, что никогда не прийдет событие завершения для такой операции?
3) Если GetQueuedCompletionStatus возвращает false, то это означает что соединение разорвано, и никаких событий завершения больше не будет? Может ли такое происходить с сокетом находящимся в listen состоянии?

На эти вопросы я не нашел внятного ответа нигде, и не удалось ничего однозначно узнать экспериментальным путем, но именно с этим связаны бесконечные глюки в моем сервере.
Может быть кто-нибудь с этим сталкивался, или можете посоветовать статью где все эти моменты досконально изложены.
Записан
sss
Специалист

ru
Offline Offline

« Ответ #1 : 18-07-2006 01:37 » 

1) Если речь идет об событиях, типа последовательных WSASend - то да. Только не понятно что значит пакет завершения. Если это означает выход потока порта из ожидания, то после двух send будет два выхода. В MS ISA Server, которая использует порты завершения, есть рекомендация, не выполнять более двух send операций без их завершения. А еще лучше выполнять одну и дожидаться появления в порте завершения. Связано с производительностью и сопротивлению атаке Denial of service.
Код:
...
    if ( WSASend( ptag->ClientSocket, &ptag->SendBuf, 1, &dwCtrl,
                  dwFlags, (WSAOVERLAPPED*) &ptag->SendOverlapped, NULL) == 0)
    {
      //Вызов был синхронным.
    }
    // Ждем проявления в порте завершения
    if (
         (WaitForSingleObjectEx(ptag->SendComplete, NETCS_MAXWAIT_SEND, TRUE) == WAIT_OBJECT_0)
         &&
         (ptag->SendError == ERROR_SUCCESS)
      )
    {
      // Дождались. Не ошибка
      fRes = TRUE;
    }


2) В случае ошибки при операции WSASend или WSARecv выход из потока все равно произойдет.
Код:

ThreadNotifyId TCPSRVAPI CTcpServer::IoPortLoop(CBaseThread* pThread)
{
   ...
    fRes = GetQueuedCompletionStatus( FIoPort, &dwNumBytes,
                                      (ULONG_PTR*) &ptag, &pOverlapped,
                                      INFINITE);
    if ( !fRes)
    {
        IoPortHandleError(ptag, pOverlapped);  //Обработка ошибки
    }
  ...
}
3) Вытекает из 2). Все зависит от типа ошибки. Разрыв соединения это не только выход по ошибке. Думаю ты знаешь, что значит принять 0 байт. Ну и, наверное, не каждая ошибка есть разрыв соединения. Допустим, сокет, привязанный к порту, в состоянии listen, и операция send завершилась неудачей (один из клиентов откинулся или таймаут), мы получаем FALSE на выходе из GetQueuedCompletionStatus. А как насчет других клиентов? Мы не перестанем получать выходы из потоков порта...

P.S.: Конечно лучше всего, это почитать Рихтера. Кстати,
у него, в операция WSASend и WSARecv на месте lpNumberOfBytesSent и lpNumberOfBytesRecvd частенько стоит NULL. НИКОГДА ТАК НЕ ДЕЛАЙ! Месяца два ловил глюки (W2K Server).

P.P.S: Недавно обсуждалась тема "Из сокета может прийти мусор?". Важно учесть ее в самом начале!
« Последнее редактирование: 18-07-2006 01:42 от sss » Записан

while (8==8)
Tiger62-vip
Гость
« Ответ #2 : 18-07-2006 05:38 » 

>1) Если речь идет об событиях, типа последовательных WSASend - то да.
Вызов WSASend идет один, но в него передается два отсылаемых буфера. Завершение происходит в большинстве случаев один раз, НО иногда почему-то два раза (проявляется только при очень высокой загрузке сервера). Из за этого сервер пытается освободить уже свободную память и падает.  Сейчас я эту проблему решил с помошью отложеного освобождения памяти (через секунду) в отдельном потоке, тоесть таким образом я ловлю повторные завершения, но решение это уж очень кривое.

>> и операция send завершилась неудачей (один из клиентов откинулся или таймаут), мы получаем FALSE на выходе из etQueuedCompletionStatus
В этом случае я закрываю соединение в котором произошла ошибка и освобождаю память. Но хотелось бы быть увереным, что потом не прийдет опять завершение по этому соединению.

> НИКОГДА ТАК НЕ ДЕЛАЙ!
Я и не делаю Улыбаюсь

>>P.P.S: Недавно обсуждалась тема "Из сокета может прийти мусор?"
Я далеко не впервые пишу сетевые приложения, и хорошо представляю работу протокола tcp. Единственно, мне непонятна логика WSASend и WSARecv. По всей видимости, логика их работы не соответствует описаной в MSDN.

З.Ы. отказ от использования AcceptEx убрал большую часть глюков, но проблема с завершением остается.
Записан
Tiger62-vip
Гость
« Ответ #3 : 18-07-2006 05:40 » 

И еще: если WSASend возвращает 0, то это означает, что завершения не будет?
Записан
sss
Специалист

ru
Offline Offline

« Ответ #4 : 18-07-2006 07:14 » 

Ты хотел сказать WSARecv возвратит размер принятых данных 0 байт? Может будет, может не будет - как поведет себя система это уже предположения. Никогда нельзя опираться на предположения. Например, я закрываю соединение с клиентом, получу я от него 0? Бывает получаю, бывает нет. Некоторые приложения клиентов выполняют shutdown, некоторые нет. Поэтому использую список ресурсов с блокировками. Надо оторвать соединение, заблокировал ресурсы, удалил ресурсы, закрыл соединение. При получении статуса, проверил наличие - есть ресурс - значит работаем.
Записан

while (8==8)
sss
Специалист

ru
Offline Offline

« Ответ #5 : 18-07-2006 07:58 » new

Я делаю так. У меня есть:
1) Список ресурсов
2) Поток acceptor
3) Поток messsenger
4) Потоки пула порта workers
5) Очередь событий (messages)
6) Список функций процесса (мesssenger)

Принцип:
При присоединении клиента, поток acceptor добавляет структуру клиента
в список ресурсов. При получении или завершении отправки данных, потоки workers добавляют события в messages. При появлении в очереди messages элемента, messsenger просыпается. В свою очередь, messsenger владеет списком callback функций процесса, использующего объект сервера. Процесс может указывать не только адрес callback функции, но и хэндл окна. В этом случае messenger вызывает функцию окна. Все это дело необходимо для сериализации сетевого потока. При возврате любой из функций кода, что событие обработано, messsenger успокаивается, а за освобождение ресурсов события отвечает эта функция. Если не одна функция не обработала события, messsenger сам удаляет ресурс события. Ресурс события состоит из кода события, буферов, ссылок на сокеты и т.д
Записан

while (8==8)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines