Использование IOCP с несколькими слушателями

Как настроить сокеты IOCP для нескольких прослушивателей (на разных портах)? Каждый пример, который я нахожу в Интернете, представляет собой пример с одним сервером и несколькими клиентами, и я не понимаю, должен ли я создавать несколько IOCP или использовать только один для всех слушателей. как-то?

Например, я нашел этот пример на GitHub, который кажется небольшой модификацией некоторого кода из Win 7 SDK, и этот код создает один порт завершения для одного сокета прослушивателя:

g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

for( DWORD dwCPU = 0; dwCPU < g_dwThreadCount; dwCPU++ )
{
    ...
    // associate worker thread with this iocp
    hThread = CreateThread(NULL, 0, WorkerThread, g_hIOCP, 0, &dwThreadId);
    ...
}

Как использовать один и тот же пул потоков для нескольких портов завершения? Или мне как-то нужен только один порт завершения для всех сокетов слушателей?


person Lou    schedule 24.03.2017    source источник
comment
Я не знаю, может ли это помочь, но у меня сохранен этот пример: serverframework.com/asynchronousevents/2012/03/ -server.html   -  person lsalamon    schedule 24.03.2017


Ответы (3)


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

Итак, у нас есть - один порт завершения, несколько рабочих потоков, прослушивающих этот порт (обычное количество потоков == количеству ядер процессора) и несколько файлов (сокетов), связанных с этим портом завершения через BindIoCompletionCallback или CreateThreadpoolIo или CreateIOCompletionPort (уродливая логика — сравните с ZwSetInformationFile(..FileCompletionInformation), который используется внутри)

person RbMm    schedule 24.03.2017

При инициализации серверной системы выполните вызов AcceptEx() для каждого порта, который должен его иметь. Используя расширенную структуру OVERLAPPED «обычным» способом, передайте как слушающие, так и клиентские сокеты потоку, который обрабатывает событие завершения ввода-вывода.

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

Нет необходимости в порте/пуле завершения для каждого слушателя, достаточно одного пула.

Поток-обработчик также должен выдать еще один AcceptEx() для подключения следующего клиента.

person ThingyWotsit    schedule 24.03.2017

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

Давайте обсудим, что такое порт завершения, как я его понимаю.

Порт завершения — это приемник событий. То есть происходит событие, порт завершения получает уведомление.

Конечно, существует большая классификация событий. Порт завершения хорошо подходит для событий определенного типа: завершения ввода-вывода. Существует патент Катлера и банды, написанный удивительно понятным для человека языком и описывающий, как IOCP интегрируется с ядром NT.

Подводя итог: порт завершения — это приемник событий, хорошо подходящий для получения уведомлений о завершении ввода-вывода.

Теперь вернемся к вопросу ОП:

Как настроить сокеты IOCP для нескольких прослушивателей (на разных портах)?

Ваше событие является завершением ранее выполненного вызова AcceptEx(). Обычно это вызов connect() на стороне клиента, но также может быть кто-то отключающий вашу сетевую карту.

сначала вы решаете, хотите ли вы, чтобы соединения на одном и том же порту обрабатывались одновременно или последовательно. Также вам нужно решить, хотите ли вы, чтобы соединения на разных портах обрабатывались одновременно или последовательно.

Я вижу три возможных подхода:

  • все завершения подключения сериализуются

CreateIOCompletionPort предварительно один раз, BindIOCompletionCallback для каждого слушателя, один рабочий поток, который вызывает GetQueuedCompletionStatus, как описано RbMm

  • завершение соединения одного и того же прослушивателя (на одном порту) сериализуется, но завершение соединения на разных портах выполняется одновременно.

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

  • все завершения соединения являются параллельными

У вас может быть один порт завершения, но вы запускаете несколько потоков для вызова GetQueuedCompletionStatus().

Вы не обязательно ограничены наличием бедер, как я описал выше. Порты завершения умны, поэтому вы можете использовать один порт и несколько официантов, а затем официанты получают блокировки для каждого прослушивателя для сериализации. Механизм порта завершения в ядре обнаружит, что вы заблокировали, и освободит других ожидающих, когда это необходимо.

Резюмируя: во-первых, вы решаете, насколько одновременным вы хотите быть. Затем вы выбираете, сколько портов завершения вам нужно иметь.

person Sergei Vorobiev    schedule 27.03.2017