IOCP 기본 구조

왜 공부했나

게임 서버에서 수천 명의 동시 접속을 처리해야 하는데, 스레드-퍼-클라이언트 방식으로는 스레드 컨텍스트 스위칭 비용이 너무 커진다. Windows에서 이를 해결하는 표준 방법이 IOCP라서 공부 시작.

핵심 개념

IOCP란

OS 커널이 I/O 완료 이벤트를 큐에 쌓아주면, 워커 스레드 풀이 꺼내서 처리하는 구조. 스레드 수를 CPU 코어 수 × 2 정도로 고정해도 수천 소켓을 처리할 수 있다.

[클라이언트들]
     ↓ (WSARecv 등록)
[IOCP 완료 큐]  ← OS 커널이 I/O 완료 시 자동 적재
     ↓ (GetQueuedCompletionStatus)
[워커 스레드 풀]  ← 고정된 수의 스레드만 사용

핵심 함수 3개

함수 역할
CreateIoCompletionPort IOCP 핸들 생성 + 소켓 연결
WSARecv / WSASend 비동기 I/O 요청 등록
GetQueuedCompletionStatus 완료 이벤트 꺼내기 (블로킹)

코드 예제

// 1. IOCP 핸들 생성
HANDLE hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

// 2. 소켓을 IOCP에 연결
CreateIoCompletionPort((HANDLE)clientSocket, hIocp, (ULONG_PTR)pSession, 0);

// 3. 비동기 수신 등록
WSABUF wsaBuf = { BUF_SIZE, pSession->recvBuf };
DWORD flags = 0;
WSARecv(clientSocket, &wsaBuf, 1, nullptr, &flags, &pSession->overlapped, nullptr);

// 4. 워커 스레드에서 완료 이벤트 처리
DWORD transferred;
ULONG_PTR key;
OVERLAPPED* pOverlapped;
GetQueuedCompletionStatus(hIocp, &transferred, &key, &pOverlapped, INFINITE);

막혔던 부분 / 삽질

  • OVERLAPPED 구조체를 스택에 두면 비동기 완료 전에 사라져서 크래시 발생 → 반드시 힙 또는 세션 객체 멤버로 관리해야 함
  • GetQueuedCompletionStatusFALSE를 반환해도 transferred == 0이면 정상 종료, 그 외는 에러 — 이 분기를 놓치면 좀비 세션 생김

정리

  • IOCP = OS가 관리하는 완료 이벤트 큐 + 고정 워커 스레드
  • 핵심은 OVERLAPPED 수명 관리 — 비동기 작업이 끝날 때까지 살아있어야 함
  • 다음: Per-IO 데이터 확장 구조 로 세션 정보를 OVERLAPPED에 붙이는 방법