winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 11.03.09 12:48
Оценка:
разрабатываю сервер обработки запросов, прочитал про настоятельно рекомендуемый Рихтером метод уведомления о завершении ввода-вывода — использование IO Completion Ports, по ходу чтения возник вопрос в контексте использования IOCP применительно к сокетам и winsock в частности — я приведу свое понимание и буду признателен если мне укажут на его возможную ошибочность :

запись в очередь порта происходит после завершения асинхронной операции ввода/вывода, т.е применительно к winsock речь идет исключительно о записи/чтении данных сокета и лишь о паре функций WSASend/WSARecv — посредством этих функций мы начинаем асинхронную запись / чтение и узнаем о завершении операции посредством IOCP. Это так ? GetQueuedCompletionStatus () вернет управление только по факту завершения операций записи/чтения, начатых функциями WSASend/WSARecv на сокете привязанном к порту ? Но если так, то получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?
Re: winsock и порты завершения ввода-вывода
От: Aspire  
Дата: 11.03.09 13:14
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:
P>запись в очередь порта происходит после завершения асинхронной операции ввода/вывода, т.е применительно к winsock речь идет исключительно о записи/чтении данных сокета и лишь о паре функций WSASend/WSARecv — посредством этих функций мы начинаем асинхронную запись / чтение и узнаем о завершении операции посредством IOCP. Это так ?

Да.

P>GetQueuedCompletionStatus () вернет управление только по факту завершения операций записи/чтения, начатых функциями WSASend/WSARecv на сокете привязанном к порту ?


Да.

P>Но если так, то получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?


Не понял, что значит "вычистка" и зачем тебе ее "параллелить".
Re[2]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 11.03.09 14:15
Оценка:
Здравствуйте, Aspire, Вы писали:

A>Здравствуйте, Pepel, Вы писали: .. получается, что параллелизм начинается с момента получения данных от клиента — мой пул потоков может лишь реагировать на факт завершения вычитки из сокета данных, отправленных мне клиентом, но как распаралелить саму вычитку при помощи IOCP ?


A>Не понял, что значит "вычистка" и зачем тебе ее "параллелить".


не вычиСтка , а вычитка , но ничего суть в чем —

вот такой расклад положим : висит accept в ожидании — обрабатывает входящие запросы на соединение, порождая при этом некоторый набор сокетов (в количестве N), мне надо из этих сокетов естественно читать данные клиентских запросов, и я так понимаю, что всю механику (весь 'изюм') IOCP я могу использовать только с момента когда стартовал WSARecv на сокетах клиентских коннектов, но чтобы этот WSARecv запустить надо создать ведь N потоков в каждом из которых этот WSARecv и стартует. Но тогда зачем IOCP если потоков и до него как мух. Так ?
Re[3]: winsock и порты завершения ввода-вывода
От: Michael Chelnokov Украина  
Дата: 11.03.09 14:52
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>висит accept


С IOCP используй AcceptEx.
И вообще, традиционно, — Windows Sockets 2.0: Write Scalable Winsock Apps Using Completion Ports.
Re[3]: winsock и порты завершения ввода-вывода
От: Aspire  
Дата: 11.03.09 14:55
Оценка:
Здравствуйте, Pepel, Вы писали:

P>не вычиСтка , а вычитка




P>вот такой расклад положим : висит accept в ожидании — обрабатывает входящие запросы на соединение, порождая при этом некоторый набор сокетов (в количестве N), мне надо из этих сокетов естественно читать данные клиентских запросов, и я так понимаю, что всю механику (весь 'изюм') IOCP я могу использовать только с момента когда стартовал WSARecv на сокетах клиентских коннектов, но чтобы этот WSARecv запустить надо создать ведь N потоков в каждом из которых этот WSARecv и стартует. Но тогда зачем IOCP если потоков и до него как мух. Так ?


А что, WSARecv без потока не стартануть? Делаешь accept, затем WSARecv и забудь об этом. Вспомнишь на обработчике IOCP.
Re[4]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 11.03.09 15:29
Оценка:
> С IOCP используй AcceptEx.

а в чем ее фишка, если не сложно в двух словах, и чем хуже вариант WSAAsyncSelect (FD_ACCEPT) + accept ?

> А что, WSARecv без потока не стартануть? Делаешь accept, затем WSARecv и забудь об этом. Вспомнишь на обработчике IOCP.


сложная конструкция, представь большой объем данных прищел, что выливается в десятки вызовов WSARecv, для каждого из которых вычитанные данные над как-то клеить и все это уже на обработчике IOCP, хотя вроде так конструкция живучая на вскидку и правдивая
Re: winsock и порты завершения ввода-вывода
От: Gomes Россия http://irazin.ru
Дата: 12.03.09 07:13
Оценка:
Надо б ФАК написать, да времени нет.

Как сказал Michael Chelnokov, использовать надо функцию AcceptEx. Первая её фишка в том, что она подготавливает сокет для асинхронного подключения, т.е. о подключении клиента ты узнаешь на GetQueuedCompletionStatus в том же самом рабочем пуле, который для операций I/O. Вторая фишка — таких сокетов можно подготовить необходимое количество, зависящее от предполагаемой нагрузки. Понятно, что после приема подключения необходимо подготовить очередной сокет. Есть и третья фишка — можно при подключении клиента сразу же принять от него данные. Это надо использовать очень осторожно, да и не думай пока об этой возможности

По общей работе. На примере Echo-сервера:
GetQueuedCompletionStatus( accept   ) - WSARecv : клиент подключился - постим запрос на прием
GetQueuedCompletionStatus( received ) - WSASend : приняли данные - постим запрос на передачу
GetQueuedCompletionStatus( sent     ) - WSARecv : передали данные - постим запрос на прием
GetQueuedCompletionStatus( received ) - WSASend : и т.д.
...

Т.е. новый запрос постим только после завершения предыдущего. Только — значит обычно. Можно запостить сразу WSARecv и WSASend, например, при написании proxy-сервера. Можно постить WSASend без уведомления о завершении, но об этом пока не думай. Зачем может понадобиться постить несколько раз подряд WSARecv, придумать не могу

У некоторых возникает вопрос — а где тут параллелизьм и производительность? Оно в работе не с одним клиентом, но со многими.
Re[2]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 12.03.09 07:32
Оценка:
Спасибо ! Прочитал в MSDN про AcceptEx(), сделал вывод, что в отличие от accept() это асинхронная функция — вызвали — она тут же вернула и гдет там на IOCP обработчике ловим ее завершение, но тогда непонятка — где ее вызывать повтороно ? если accept() блокирует и ее можно успешно в цикде повесить, то как быть с AcceptEx()?

> Зачем может понадобиться постить несколько раз подряд WSARecv, придумать не могу


если относительно большой объем данных клиент шлет, даже пару десятков Kb уже наверняка одним вызовом WSARecv не обойтись, или же я не догоняю и уведомление о завершении вычитки станет в IOCP толко когда все данные из сокета будут получены (положим что передаваемый нами в WSARecv буфер приемки достаточно велик) ?
Re[3]: winsock и порты завершения ввода-вывода
От: Gomes Россия http://irazin.ru
Дата: 12.03.09 07:50
Оценка: 3 (1)
Здравствуйте, Pepel, Вы писали:

P>Спасибо !

Спасибо здесь сверху справа

P>где ее вызывать повтороно ? если accept() блокирует и ее можно успешно в цикде повесить, то как быть с AcceptEx()?

Клиент подключился — создал новый сокет и вызвал AcceptEx.

P>если относительно большой объем данных клиент шлет, даже пару десятков Kb уже наверняка одним вызовом WSARecv не обойтись, или же я не догоняю и уведомление о завершении вычитки станет в IOCP толко когда все данные из сокета будут получены (положим что передаваемый нами в WSARecv буфер приемки достаточно велик) ?

WSARecv — GetQueuedCompletionStatus — WSARecv — GetQueuedCompletionStatus ...

И нету никаких "всех данных". Для системы есть только поток. Только ты определяешь что такое данные, посредством протокола.
Re[4]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 12.03.09 08:24
Оценка:
Gomes, отлично, тогда правильно ли я понимаю следующее :

в такой конструкции :

первый вызов AcceptEx() должен происходить не в обработчике IOCP, а все последующие — именно в потоке обработчика IOCP

WSAAsyncSelect (FD_READ) при таком подходе не нужен, требуется лишь просто вызвать WSAAsyncSelect() для декларации перехода в асинхронный режим работы, а дальше WSARecv() гонять

тогда вопрос, а если я стартану WSARecv() вхолостую, т.е. на сокете в котором нет данных для вычитки, запись в очередь IOCP станет когда данные появятся , или же WSARecv() вернет ошибку ?
Re[5]: winsock и порты завершения ввода-вывода
От: Gomes Россия http://irazin.ru
Дата: 12.03.09 08:55
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>первый вызов AcceptEx() должен происходить не в обработчике IOCP, а все последующие — именно в потоке обработчика IOCP

Да. Причем первый раз можно (нужно) вызвать некоторое количество раз, чтобы подготовить достаточное количество сокетов (если планируется большая нагрузка).

P>WSAAsyncSelect (FD_READ) при таком подходе не нужен, требуется лишь просто вызвать WSAAsyncSelect() для декларации перехода в асинхронный режим работы, а дальше WSARecv() гонять

Забудь про WSAAsyncSelect. Создавай сокет так: WSASocket( PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED )

P>тогда вопрос, а если я стартану WSARecv() вхолостую, т.е. на сокете в котором нет данных для вычитки, запись в очередь IOCP станет когда данные появятся , или же WSARecv() вернет ошибку ?

Ну а как ты думаешь, стоило бы огород городить если б ошибку возвращало?
Re[6]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 12.03.09 09:06
Оценка:
Спасибо, чет вырисовывается в голове, сейчас на бумагу накидаю.
Re[6]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 12.03.09 12:33
Оценка:
по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?
Re[7]: winsock и порты завершения ввода-вывода
От: Gomes Россия http://irazin.ru
Дата: 12.03.09 12:48
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?

Закрывает серверный сокет.
Re[7]: winsock и порты завершения ввода-вывода
От: Pasha1st  
Дата: 12.03.09 17:45
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>по ходу интересно стало — поскольку accept() блокирует поток, то как народ этот поток завершает корректно ?

Можно использовать неблокирующий сокет для accept(), и/или select() на пару сокетов (серверный+мониторный, на мониторный посылаются данные чтобы прервать ожидание) либо с таймаутом. Это все безотносительно темы треда.
Re[5]: winsock и порты завершения ввода-вывода
От: Michael Chelnokov Украина  
Дата: 12.03.09 18:16
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

>> С IOCP используй AcceptEx.

P>а в чем ее фишка, если не сложно в двух словах, и чем хуже вариант WSAAsyncSelect (FD_ACCEPT) + accept ?

В том что по завершению AcceptEx приходит уведомление в порт, что полностью совместимо с остальной логикой работы программы, а не окошечное сообщение в какой-то непонятно зачем сделанный отдельный поток. Почему вы постоянно пытаетесь скрестить ужа и ежа?
Re[6]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 13.03.09 07:28
Оценка:
спасибо всем, проникся действительно winsock+IOCP это могучий подход, пока архитектуру рисую — вот по ходу еще вопрос возник — стартанул я WSARecv()на некотором клиентском сокете и пошел себе дальше, но представим, что клиент на данном сокете закрыл соединение (пусть даже не отправляя мне ничего вообще), — в таком случае в очередь завершения IOCP станет запись с кодом ошибки на сокете , так ?
Re[7]: winsock и порты завершения ввода-вывода
От: Michael Chelnokov Украина  
Дата: 13.03.09 10:36
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>но представим, что клиент на данном сокете закрыл соединение (пусть даже не отправляя мне ничего вообще), — в таком случае в очередь завершения IOCP станет запись с кодом ошибки на сокете , так ?


Да, обязательно. Можешь на это рассчитывать.
Re[8]: winsock и порты завершения ввода-вывода
От: Pepel Беларусь  
Дата: 16.03.09 09:55
Оценка:
по коду хочу сверить часы :

есть IOCP и есть привязанный к нему сокет MYSOCK, на котором последовательно стартовал серию асинхронных вызовов : ConnectEx, WSARecv, WSASend, дальше на обработчике IOCP ловлю завершение стартованных мной операций,

вопрос в механизме получения мной информации о том, какой из вызовов завершился, понял, что наиболее подходящий вариант да и похоже единственный (!) — параметр CompletionKey функции CreateIoCompletionPort() его тип ULONG_PTR что позволяет передавать даже адрес объекта, но вот народ что именно туда лепит ? какие идентификаторы ? структура OVERLAPPED тоже обилием пространтсва для таких маневров не блещет — только поле HANDLE hEvent, что мало да и c типом HANDLE над обращаться осторожно насколько понимаю. К тому же если сокетов привязоно к IOCP во множественном количестве — на каком из них событие произошло ? Пока, единственное, что пришло на ум — в CompletionKey при привязке сокета к IOCP передавать хэндл сокета либо адрес некой собственной конструкции(структуры), а в OVERLAPPED.hEvent — код операции (закодировать целочисленным набором основные вызовы свои : ConnectEx — 0, WSARecv — 1, .. ).
Поправьте меня, если я отклонился от курса в понимании .
Re[9]: winsock и порты завершения ввода-вывода
От: Gomes Россия http://irazin.ru
Дата: 16.03.09 11:39
Оценка: 2 (1)
Здравствуйте, Pepel, Вы писали:

P>есть IOCP и есть привязанный к нему сокет MYSOCK, на котором последовательно стартовал серию асинхронных вызовов : ConnectEx, WSARecv, WSASend, дальше на обработчике IOCP ловлю завершение стартованных мной операций,

ConnectEx — это ты асинхронного клиента пишешь что-ли? В любом случае, шли запросы последовательно, после завершения предыдущего. Когда разберешься — будешь слать как захочешь

P>вопрос в механизме получения мной информации о том, какой из вызовов завершился, понял, что наиболее подходящий вариант да и похоже единственный (!) — параметр CompletionKey функции CreateIoCompletionPort() его тип ULONG_PTR что позволяет передавать даже адрес объекта, но вот народ что именно туда лепит ? какие идентификаторы ?

Я в CompletionKey задаю для сокетов 2 параметра: COMPKEY__CONNECT для серверного и COMPKEY__IO для клиентского. Всю остальную информацию в структуре, производной от OVERLAPPED для каждой операции.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.