IOCP: уведомления без копирования байтов

У меня есть приложение IOCP, которое хранит буфер размером 64 КБ для каждого контекста сокета. Он использует много оперативной памяти, обрабатывая тысячи сокетов. Вместо этого я хочу переключиться на модель, в которой у меня есть буфер размером 64 КБ на контекст iocp-потока (например, я могу сделать это в epoll и kqueue). Для этого мне нужно, чтобы мой порт завершения мог получать уведомления без копирования байтов в предоставленный WSABUF, а после уведомления просто вызывайте async WSARecvFrom (без предоставления перекрывающейся структуры, я использую udp для тестов), пока я не получу WSAEWOULDBLOCK. Я читал, что следующий метод может быть реализован, если я предоставлю пустой WSABUF (buf = NULL, len = 0) для вызова WSARecvFrom с перекрывающейся структурой. Но это не работает: IOCP никогда не «просыпается», так как буфер слишком мал.

Есть ли другие способы реализовать такой сценарий?


person user1266334    schedule 17.05.2012    source источник


Ответы (1)


Это отлично работает для TCP, и это хороший способ избежать ограничения «заблокированных страниц», которое было большой проблемой в более ранних версиях Windows, но, похоже, сейчас не представляет такой проблемы. Он должен отлично работать в вашей ситуации.

У меня есть набор примеров кода IOCP, который можно загрузить отсюда и это поддерживает «чтение нулевого байта» и последующие асинхронные чтения (то есть он выдает перекрывающееся чтение с буфером только после завершения чтения с нулевым байтовым буфером), мой последний код поддерживает как синхронное, так и асинхронное чтение после завершения чтения нулевого байта .

Обратите внимание, что если вы имеете дело с TCP, вы упомянули, что используете UDP «для тестов», тогда вы можете достичь «единого буфера», выполняя только одно перекрывающееся чтение для каждого соединения. Как только это чтение завершится, вы можете обработать его, прежде чем выдавать новое перекрывающееся чтение с использованием того же буфера ...

«Считывание нулевого байта» по-прежнему полезно, если вам нужно иметь дело со многими тысячами одновременных соединений, когда соединения отправляют данные нечасто, поскольку это сокращает количество страниц, заблокированных за один раз, до минимума. Использование его для уменьшения использования памяти потребует, чтобы вы выделяли буфер для соединения только после завершения «чтения нулевого байта».

person Len Holgate    schedule 17.05.2012
comment
Спасибо за ответ. А как насчет UDP? Мое приложение обрабатывает как TCP, так и UDP-соединения, и я хочу иметь единую архитектуру. Можно ли по UDP? - person user1266334; 18.05.2012
comment
С UDP не пробовал. Я ожидаю, что ввод-вывод завершится с еще одной ошибкой данных, и вы не сможете получить остальную часть дейтаграммы. Вы действительно ожидаете датаграмм UDP размером 64К? Я предполагаю, что вы не используете какие-либо маршрутизаторы, и это прямое локальное соединение? - person Len Holgate; 18.05.2012
comment
Ввод-вывод никогда не завершается при чтении нулевого байта. Если я поставлю 1-байтовый буфер, он также никогда не завершится. Однако, если я поставлю буфер размером 64 КБ, он будет работать нормально. Максимальный размер дейтаграммы UDP составляет 64 КБ, так что да, я ожидаю такие дейтаграммы. Я делаю тесты на localhost, но мое приложение, конечно же, будет работать в Интернете - person user1266334; 18.05.2012
comment
Странно, что он никогда не завершается, хотя я видел другие проблемы с IOCP и UDP (правда, только когда включен FILE_SKIP_COMPLETION_PORT_ON_SUCCESS (см. lenholgate.com/blog/2010/01/). ИМХО вам рекомендуется прочитать это: stackoverflow.com/questions/10355626/ относительно отправки больших дейтаграмм UDP через Интернет. Я полагаю, что вы, вероятно, сочтете такие большие дейтаграммы очень ненадежными из-за потери пакетов. - person Len Holgate; 18.05.2012
comment
libuv, похоже, принимает нулевые байты на сокеты UDP. Их уловка заключается в использовании флага MSG_PEEK, чтобы получение нулевого байта не потребляло ожидающий пакет. - person Nathaniel J. Smith; 03.02.2017