IOCP + сокеты - давайте докопаемся до истины.
От: Diablo_II  
Дата: 24.12.09 05:57
Оценка:
Приветствую всех дамы и господа.
Возникла необходимость написания TCP сервера с использованием IOCP.
В связи с этим возник ряд вопросов ответы на которые на удалось выяснить путём прокапывания форума и гугления.
Вопросы касаются кодов завершения сокетных функций на IOCP + вообще правильный ли подход к обработке?
Извиняюсь за количество текста — но треба разобраться до конца, что же и когда мы получаем на IOCP.

Итак — в общем минимальном случае имеем сокет + IOCP + рабочий поток.
Стратегия работы сводится к комбинации асинхронных вызовов
— AcceptEx
— WSARect
— WSASend
— TransmitFile
— DisconnectEx (или её эмуляция через TransmitFile)

Результаты выполнения этих методов сваливаются на IOCP, где после обработки происходит новый вызов одного из методов.

С кодом делающим непосредственно вызов одного из методов более менее понятно. Выглядит примерно так:

 if(WSARecv(...) == SOCKET_ERROR)
 {
    DWORD dwErr = WSAGetLastError();
    if(dwErr == WSA_IO_PENDING) 
    { 
      ... /* Нормально - процесс пошёл. Результат будет на IOCP. */ 
    }
    else if(dwErr == WSAENOTCONN и пр.) 
    {
       /* Обработка ошибки - результат на IOCP не придёт. */
    }    
 }
 else
 {
    /* Выполнилась синхронно - всё равно на порт свалится. */
 }


Теперь код обработки на IOCP:

   ...
   DWORD dwErr = ERROR_SUCCESS;

   BOOL res = iocp.GetStatus(&NumberOfBytes, &pContext, (LPOVERLAPPED*)&pOlv, INFINITE);

   // Есть ли ошибки на порте?
   if(res == FALSE)
   {                       
      if(pOlv == NULL)
      {
         // Ошибка связана с самим портом ввода вывода.
         dwErr = GetLastError();

         // обработать - скорее всего прибить поток или сервер.
         return; 
      }

      // Ошибка связана с операцией ввода/вывода.
      dwErr = WSAGetLastError();
   }
   // Далее обработка Completion одной из сокетных функций (выполнение которой завершилось с кодом в dwErr)
   ...


Теперь код обработки Completion + вопросы.
У нас возможно несколько развитий событий:
— Мы сделали closesocket(...) — какие возможные варианты кодов в dwErr?
— Мы сделали DisconnectEx(...) — какие возможные варианты кодов в dwErr?
— Удалённый узел сделал closesocket(...) — какие возможные варианты кодов в dwErr?
— Удалённый узел сделал shutdown(...) + closesocket(...) — какие возможные варианты кодов в dwErr?
— Ещё какие возможны варианты?

Итак — конкретно по процедурам завершения:

  AcceptEx-Completion (dwErr, NumberOfBytes)
  {
    if(dwErr == ERROR_NETNAME_DELETED)
    {
       /* Уделённый хост закрыл соединение раньше чем мы приняли его. AcceptEx ждал первых данных. */
       /* Какие ещё коды могут означать тоже самое? */
    }
    else if(dwErr == ERROR_OPERATION_ABORTED || ERROR_CANCELLED || ERROR_SEM_TIMEOUT)
    {
       /* Мы сделали closesocket - наверное останавливаем сервак */
       /* все ли коды правильные или возможны ещё какие ? или какие есть лишние ? */
       /* на разных ОС могут быть разные ? */
    }
    else if(dwErr == ERROR_SUCCESS)
    {
       /* 1. NumberOfBytes != 0 - соединение принято и есть первые данные - всё ОК */
       /* 2. NumberOfBytes == 0 - соединение было принято в ядре, но AcceptEx ждал первых данных - но не дождался - клиент закрыл соединение */
       /* 3. NumberOfBytes == 0 - соединение было принято в ядре, но AcceptEx ждал первых данных - но не дождался - мы сами сделали DisconnectEx (закрываем по таймауту) */
       /* как в случае 2 отличить, если AcceptEx НЕ ждал первых данных? */
       /* почему так свалилось, а не через ERROR_NETNAME_DELETED? */
       /* как отличить 2 и 3? */
    }
    else if(dwErr == ...)
    {
       /* Что ещё могут быть за коды возврата в каких-то ситуациях различного поведения? */
       /* Как их различать если один и тот же код на разные ситуации ? */
    }
    else
    {
       /* Прочие нештатные коды - говорящие - надо прибить соединение и/или сокет */
    }

  }




  WSARecv/WSASend/TransmitFile-Completion (dwErr, NumberOfBytes)
  {
    if(dwErr == ERROR_NETNAME_DELETED)
    {
       /* Уделённый хост закрыл соединение. */
       /* Какие ещё коды могут означать тоже самое? */
    }
    else if(dwErr == ERROR_OPERATION_ABORTED || ERROR_CANCELLED || ERROR_SEM_TIMEOUT)
    {
       /* Мы сделали closesocket - наверное останавливаем сервак */
       /* все ли коды правильные или возможны ещё какие ? или какие есть лишние ? */
       /* на разных ОС могут быть разные ? */
    }
    else if(dwErr == ERROR_SUCCESS)
    {
       /* 1. NumberOfBytes != 0 - данные отправлены/получены - всё ОК */
       /* 2. NumberOfBytes == 0 - данных нет - клиент закрыл соединение */
       /* 3. NumberOfBytes == 0 - данных нет - мы сами сделали DisconnectEx (закрываем соединение) */
       /* как отличить 2 и 3? */
       /* Какие ещё коды могут означать тоже самое? */
    }
    else if(dwErr == ...)
    {
       /* Что ещё могут быть за коды возврата в каких-то ситуациях различного поведения? */
       /* Как их различать если один и тот же код на разные ситуации ? */
    }
    else
    {
       /* Прочие нештатные коды - говорящие - надо прибить соединение и/или сокет */
    }

  }



Попутные вопросы:
Что делает setsockopt с SO_UPDATE_ACCEPT_CONTEXT — что именно он обновляет в сокете?
Что делает setsockopt с SO_UPDATE_CONNECTION_CONTEXT — что именно он обновляет в сокете?

Заранее благодарю за разъяснения и за указание на прочие возможные ситуации и косяки.
Ден.
Re: IOCP + сокеты - давайте докопаемся до истины.
От: Gomes Россия http://irazin.ru
Дата: 24.12.09 11:24
Оценка:
Нельзя использовать AcceptEx с первым приемом, по причине возможного отсутствия первой посылки.
По остальному надо поглядеть повнимательней, но как-то дюже много разных проверок на коды ошибок.
Re[2]: IOCP + сокеты - давайте докопаемся до истины.
От: Diablo_II  
Дата: 24.12.09 11:36
Оценка:
Здравствуйте, Gomes, Вы писали:

G>Нельзя использовать AcceptEx с первым приемом, по причине возможного отсутствия первой посылки.


Не согласен.
AcceptEx может ждать, а может не ждать первых данных — это зависит от прикладного протокола по которому работает сервер.
Например, для HTTP — клиент обязан первым послать данные. Мы получим их вместе с AcceptExComplete.
Если клиент просто висит — он будет отключен по таймауту в друом потоке.

Если AcceptEx не ждёт первых данных — ну тут всё понятно — AcceptExComplete, как только клиент подключился.

G>По остальному надо поглядеть повнимательней, но как-то дюже много разных проверок на коды ошибок.


Проверок не много. Есть необходимость чётко знать, что происходит:
— Локальный дисконект (сервер отключает клиента следуя своей логике)
— Удалённый дисконект (клиента отключается от сервера)
— Сервер останавливается
— Ошибки сокетов
— Клиент подключился и сразу отключился (события то сработаю, а как их задетектить, по каким сочетаниям параметров в Completion?) — реального то соединения уже нет.
— Клиент подключился и был отклонён по таймауту — реального то соединения тоже уже нет.
Re[3]: IOCP + сокеты - давайте докопаемся до истины.
От: Michael Chelnokov Украина  
Дата: 24.12.09 19:32
Оценка:
Здравствуйте, Diablo_II, Вы писали:

D_I>Проверок не много. Есть необходимость чётко знать, что происходит:


Настоятельно рекомендую не привязываться к конкретным кодам ошибок. Обычно стараются свести лишь к определению успеха/неудачи. Иначе придется обрабатывать слишком много случаев и все это может перестать работать в следующей версии системы.

D_I> — Локальный дисконект (сервер отключает клиента следуя своей логике)

D_I> — Удалённый дисконект (клиента отключается от сервера)

Активные операции WSASend/WSARecv обламываются (например, с WSAECONNABORTED в первом случае, и с WSAECONNRESET во втором). Если таких операций в данный момент нет, то ничего не происходит. При дальнейшем использовании хэндла сокета будет WSAENOTSOCK, кажется.

D_I> — Сервер останавливается


Т.е. закрывается слушающий сокет? Висящие AcceptEx отвалятся с ошибкой (не помню какой, легко устанавливается экспериментом).

D_I> — Ошибки сокетов


Соответствующий код ошибки активных операций будет возвращен через порт. Например, WSAENETDOWN.

D_I> — Клиент подключился и сразу отключился (события то сработаю, а как их задетектить, по каким сочетаниям параметров в Completion?) — реального то соединения уже нет.


AcceptEx успешно пройдет, но последующий вызов WSASend/WSARecv вернет ошибку (например, WSAENOTCONN или WSAECONNRESET, не помню точно). Если при AcceptEx идет попытка принять данные, то ошибку вернет уже он.

D_I> — Клиент подключился и был отклонён по таймауту — реального то соединения тоже уже нет.


Кем отклонен? Если системой, то тоже, что и в первых двух случаях, но с ошибкой WSAETIMEDOUT. Если вашим приложением, то это первый вариант, WSAECONNABORTED.
Re[4]: IOCP + сокеты - давайте докопаемся до истины.
От: Diablo_II  
Дата: 24.12.09 19:51
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

MC>Здравствуйте, Diablo_II, Вы писали:


D_I>>Проверок не много. Есть необходимость чётко знать, что происходит:


MC>Настоятельно рекомендую не привязываться к конкретным кодам ошибок. Обычно стараются свести лишь к определению успеха/неудачи. Иначе придется обрабатывать слишком много случаев и все это может перестать работать в следующей версии системы.

Тут согласен — вопрос не в кодах ошибок, а в том как распознать ситуацию.
Ждущий AcceptEx завершается с некими параметрами на IOCP.
Как чётко отделить локальное закрытие сокета (я сам его закрыл в другом потоке) от удалённого закрытия сокета (клиент вызвал closesocket).

По поводу этого кстати заметил в ходе экспериментов следующие 2 ситуации.
— В сервере висит запрос AcceptEx запрос с ожиданием данных.
— Если мой клиент подключается и потом не посылая данных делает closesocket.
— AcceptEx сваливается на IOCP c кодом ERROR_SUCCESS и dwNumberBytes == 0.
причём самое смешное — на этот сокет — по факту уже не имеющий подключения можно сделать WSARecv — который отработает без ошибок.
Правда для него всегда будет dwNumberBytes == 0 возвращаться.

но что странно — взял netcat.exe (всем не безизвестный)
— В сервере висит запрос AcceptEx запрос с ожиданием данных.
— netcat'ом устанавливаем соединенеие и рвём его не отправляя данных.
— AcceptEx сваливается на IOCP c кодом ERROR_NETNAME_DELETED.

Откуда такая разница в поведении? Вроде оба делают судя по коду closesocket?

D_I>> — Локальный дисконект (сервер отключает клиента следуя своей логике)

D_I>> — Удалённый дисконект (клиента отключается от сервера)

MC>Активные операции WSASend/WSARecv обламываются (например, с WSAECONNABORTED в первом случае, и с WSAECONNRESET во втором). Если таких операций в данный момент нет, то ничего не происходит. При дальнейшем использовании хэндла сокета будет WSAENOTSOCK, кажется.


На сколько я понял ошибки типа WSAECONNABORTED, WSAENOTSOCK, WSAECONNRESET и пр. WSAxxx возвращаются только при немосредственном вызове WSARecv и пр функций.
Т.е. они никогда (я такого не разу не видел) на возвращаются как ошибки при завершении операции через IOCP.
При завершении опрации через IOCP вываливаются другие коды типа ERROR_NETNAME_DELETED, ERROR_OPERATION_ABORTED и пр.

D_I>> — Сервер останавливается


MC>Т.е. закрывается слушающий сокет? Висящие AcceptEx отвалятся с ошибкой (не помню какой, легко устанавливается экспериментом).


D_I>> — Ошибки сокетов


MC>Соответствующий код ошибки активных операций будет возвращен через порт. Например, WSAENETDOWN.

как уже сказал выше WSAxxx — никогда на прт не валяться? или я не прав? если не прав то как воспроизвести?
У меня до этого ни разу не получилось.

D_I>> — Клиент подключился и сразу отключился (события то сработаю, а как их задетектить, по каким сочетаниям параметров в Completion?) — реального то соединения уже нет.


MC>AcceptEx успешно пройдет, но последующий вызов WSASend/WSARecv вернет ошибку (например, WSAENOTCONN или WSAECONNRESET, не помню точно). Если при AcceptEx идет попытка принять данные, то ошибку вернет уже он.

Всё тот же WSAххх который невозможно увидеть при завершении через IOCP.

D_I>> — Клиент подключился и был отклонён по таймауту — реального то соединения тоже уже нет.


MC>Кем отклонен? Если системой, то тоже, что и в первых двух случаях, но с ошибкой WSAETIMEDOUT. Если вашим приложением, то это первый вариант, WSAECONNABORTED.

Всё тот же WSAххх который невозможно увидеть при завершении через IOCP.

Вот так... фигня какая-то.
Или руки совсем кривеникие стали у меня.
Re: IOCP + сокеты - давайте докопаемся до истины.
От: Pepel Беларусь  
Дата: 24.12.09 20:43
Оценка:
Здравствуйте, Diablo_II, Вы писали:

IOCP + сокеты — давайте докопаемся до истины.

уже докопались и не раз, поиском воспользуйтесь ..

http://rsdn.ru/forum/network/3322548.1.aspx
Автор: Pepel
Дата: 11.03.09
Re[2]: IOCP + сокеты - давайте докопаемся до истины.
От: Аноним  
Дата: 24.12.09 20:48
Оценка:
Здравствуйте, Pepel, Вы писали:

P>Здравствуйте, Diablo_II, Вы писали:


P>IOCP + сокеты — давайте докопаемся до истины.


P>уже докопались и не раз, поиском воспользуйтесь ..


P>http://rsdn.ru/forum/network/3322548.1.aspx
Автор: Pepel
Дата: 11.03.09


Видел вашу тему, и даже прочёл несколько раз. ответы на свои вопросы так и не нашёл.
Возможно вы разобрались потом со всем, может подскажете что на IOCP сваливается в описанных выше ситуациях и как их раздичать?
Re[5]: IOCP + сокеты - давайте докопаемся до истины.
От: Michael Chelnokov Украина  
Дата: 24.12.09 21:20
Оценка:
Здравствуйте, Diablo_II, Вы писали:

D_I>Как чётко отделить локальное закрытие сокета (я сам его закрыл в другом потоке) от удалённого закрытия сокета (клиент вызвал closesocket).


Никогда не задавался таким вопросом. Что это у тебя за реализация сервера, если ты сам не знаешь, что закрыл сокет?

D_I>По поводу этого кстати заметил в ходе экспериментов следующие 2 ситуации.

D_I>- В сервере висит запрос AcceptEx запрос с ожиданием данных.
D_I>- Если мой клиент подключается и потом не посылая данных делает closesocket.
D_I>- AcceptEx сваливается на IOCP c кодом ERROR_SUCCESS и dwNumberBytes == 0.
D_I> причём самое смешное — на этот сокет — по факту уже не имеющий подключения можно сделать WSARecv — который отработает без ошибок.
D_I> Правда для него всегда будет dwNumberBytes == 0 возвращаться.

Все логично. Соединение установлено успешно (AcceptEx отработал без ошибок) и затем EOF на WSARecv.

D_I>но что странно — взял netcat.exe (всем не безизвестный)

D_I>- В сервере висит запрос AcceptEx запрос с ожиданием данных.
D_I>- netcat'ом устанавливаем соединенеие и рвём его не отправляя данных.
D_I>- AcceptEx сваливается на IOCP c кодом ERROR_NETNAME_DELETED.
D_I>Откуда такая разница в поведении? Вроде оба делают судя по коду closesocket?

Посмотри снифером, какие пакеты отсылает твой клиент, и какие — netcat.

D_I>На сколько я понял ошибки типа WSAECONNABORTED, WSAENOTSOCK, WSAECONNRESET и пр. WSAxxx возвращаются только при немосредственном вызове WSARecv и пр функций.

D_I>Т.е. они никогда (я такого не разу не видел) на возвращаются как ошибки при завершении операции через IOCP.
D_I>При завершении опрации через IOCP вываливаются другие коды типа ERROR_NETNAME_DELETED, ERROR_OPERATION_ABORTED и пр.

Возможно. Именно поэтому и не рекомендуется привязываться к определенным кодам ошибок.
Re[6]: IOCP + сокеты - давайте докопаемся до истины.
От: Diablo_II  
Дата: 25.12.09 05:19
Оценка:
Здравствуйте, Michael Chelnokov, Вы писали:

D_I>>Как чётко отделить локальное закрытие сокета (я сам его закрыл в другом потоке) от удалённого закрытия сокета (клиент вызвал closesocket).

MC>Никогда не задавался таким вопросом. Что это у тебя за реализация сервера, если ты сам не знаешь, что закрыл сокет?
Ну тут хитрость была с том чтобы без дополнительных флажков и пр. приспособлений определить чьё это закрытие.
Так, например, если висит AcceptEx c ожиданием данных, то если клиент подключится и не присылая данных отключится — получим
AcceptExComplete на IOCP c кодом ERROR_SUCCESS и dwNumberBytes == 0
а если я сам закрою сокет через Disconnect, то плучим то же
AcceptExComplete на IOCP c кодом ERROR_SUCCESS и dwNumberBytes == 0.

В общем решение было изначально с флажком, но что-то оно мне не нравилось. думал как-то по другому устаканить.


D_I>>По поводу этого кстати заметил в ходе экспериментов следующие 2 ситуации.

D_I>>- В сервере висит запрос AcceptEx запрос с ожиданием данных.
D_I>>- Если мой клиент подключается и потом не посылая данных делает closesocket.
D_I>>- AcceptEx сваливается на IOCP c кодом ERROR_SUCCESS и dwNumberBytes == 0.
D_I>> причём самое смешное — на этот сокет — по факту уже не имеющий подключения можно сделать WSARecv — который отработает без ошибок.
D_I>> Правда для него всегда будет dwNumberBytes == 0 возвращаться.

MC>Все логично. Соединение установлено успешно (AcceptEx отработал без ошибок) и затем EOF на WSARecv.

Вот оно Блин, как-то не догадался рассматривать WSARecvComplete c dwNumberBytes = 0 как EOF.
Теперь всё логично. Спасибо за очевидную мысль.

D_I>>На сколько я понял ошибки типа WSAECONNABORTED, WSAENOTSOCK, WSAECONNRESET и пр. WSAxxx возвращаются только при непосредственном вызове WSARecv и пр функций.

D_I>>Т.е. они никогда (я такого не разу не видел) на возвращаются как ошибки при завершении операции через IOCP.
D_I>>При завершении опрации через IOCP вываливаются другие коды типа ERROR_NETNAME_DELETED, ERROR_OPERATION_ABORTED и пр.

MC>Возможно. Именно поэтому и не рекомендуется привязываться к определенным кодам ошибок.

ну на ERROR_SUCCESS то хоть можно полижиться?
Re[7]: IOCP + сокеты - давайте докопаемся до истины.
От: xneo Украина  
Дата: 17.03.10 09:15
Оценка:
Здравствуйте.
Тоже пришлось связаться с написанием сервера с использованием IOCP.
Вроде работает но есть один неприятный момент. Мне нужно держать большое количество подключённых клиентов и иногда рассылать всем им одновременно некоторые данные. Пробовал подключить 2000-3000 сокетов к серверу и разослать всем по 4кб данные (отдаю кусками по 512-2048 байт). В очередь IOCP сразу же приходит уведомление что данные успешно отосланы не дожидаясь отправки. Т.е. выходит что ОС (Windows XP SP3) забирает все эти ~4000 пакетов по 1024байта себе в буфер и говорит что "всё отосланно" а дальше продолжает их отсылать. Сеть 100мбит, нагрузка сразу прыгает до 25% потом обратно падает до 2-5% и продолжает неспеша отдавать эти пакеты.
Может кто в курсе почему так получается ? Буду очень признателен
Re[8]: IOCP + сокеты - давайте докопаемся до истины.
От: Michael Chelnokov Украина  
Дата: 18.03.10 21:17
Оценка:
Здравствуйте, xneo, Вы писали:

X>Может кто в курсе почему так получается ?


Nagle's algorithm
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.