Ответ
I/O Completion Ports (IOCP) — это механизм асинхронного ввода-вывода в Windows, предназначенный для создания высокопроизводительных масштабируемых сетевых серверов и приложений, работающих с множеством одновременных соединений.
Принцип работы:
Вместо того чтобы блокировать поток на операции ввода-вывода (как в синхронной модели) или использовать множество потоков для ожидания событий (как в модели select/poll), IOCP уведомляет приложение о завершении операции. Приложение связывает сокеты (или файлы) с портом завершения, а затем инициирует асинхронные операции (например, WSARecv, WSASend). Когда операция завершается, система помещает уведомление о завершении в очередь порта. Потоки из специального пула (рабочие потоки) ожидают на этом порту (GetQueuedCompletionStatus) и обрабатывают результаты.
Пример структуры для передачи контекста операции:
#include <winsock2.h>
#include <windows.h>
#include <memory>
struct PerIoData {
WSAOVERLAPPED overlapped; // Обязательная структура для асинхронных операций
WSABUF wsaBuf; // Буфер для данных
char buffer[1024];
SOCKET socket; // Сокет, связанный с операцией
// ... другие поля контекста
};
// Создание порта завершения
HANDLE completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
// Связывание сокета с портом
CreateIoCompletionPort((HANDLE)clientSocket, completionPort, (ULONG_PTR)contextPtr, 0);
// Инициирование асинхронного чтения
auto perIoData = std::make_unique<PerIoData>();
perIoData->wsaBuf.buf = perIoData->buffer;
perIoData->wsaBuf.len = sizeof(perIoData->buffer);
perIoData->socket = clientSocket;
DWORD flags = 0;
WSARecv(clientSocket, &(perIoData->wsaBuf), 1, NULL, &flags, &(perIoData->overlapped), NULL);
// Рабочий поток: ожидание и обработка завершённых операций
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED overlapped;
while (GetQueuedCompletionStatus(completionPort, &bytesTransferred, &completionKey, &overlapped, INFINITE)) {
auto data = reinterpret_cast<PerIoData*>(overlapped);
// Обработка данных в data->buffer, bytesTransferred содержит количество прочитанных байт
// ...
}
Преимущества для C++ серверов:
- Масштабируемость: Эффективное управление тысячами соединений с небольшим числом потоков (обычно по числу процессорных ядер).
- Минимизация контекстных переключений: Система будит ровно столько потоков, сколько требуется для обработки завершённых операций.
- Гибкость: Позволяет связывать произвольный контекст (completion key) с операцией для упрощения логики обработки.
В экосистеме Unix/Linux аналогичную роль выполняют механизмы epoll и io_uring.