[Server] IOCP
Input/Output Completion Port (IOCP)
● Windows 운영 체제에서 비동기 입출력 작업을 처리하기 위한 작동 과정 중 하나
● 고성능 네트워크 서버나 파일 처리 애플리케이션에서 사용됨
● 적은 수의 스레드로 다수의 클라이언트 요청을 효율적으로 처리할 수 있도록 설계된
이벤트 기반 큐 시스템
Overlapped I/O?
● Overlapped, 중첩 된다는 뜻으로, 하나의 스레드가 한 번에 하나의 I/O만을 처리하는 것이 아닌,
여러 개의 IO를 다루는 기술
● 기존 동기식 IO는 하나 요청 한 후 완료될 때까지 기다려야함
→ 비동기 IO 사용하면, 입출력 요청 보낸 이후 나중에 그 입출력 결과 받을 수 있다.
Overlapped I/O → IOCP
● IOCP는 Overlapped I/O에서 사용하는 'OVERAPPED' 구조체와 관련 함수들을 그대로 사용
→ 비동기 작업 완료 정보를 관리하고 처리 가능
● Overlapped I/O는 작업 완료 여부를 확인하기 위해 호출 스레드가 직접 확인 or 대기
→ 예) WSAaitForMultipleEvents
● 반면 IOCP는 완료된 정보를 Completion Port에 등록하고, Worker Thread Pool에서
이를 처리하는 방식으로 동작
● 즉, IOCP는 Overlapped I/O의 단점을 보완한 고성능 모델
예시 코드로 맛만...
1. IOCP 오브젝트 생성
- 'CreateIoCompletionPort' 함수를 호출 → IOCP 오브젝트 생성
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
2. 디바이스(소켓, 파일 등)와 IOCP 연결
- 입출력을 수행할 디바이스를 IOCP와 연결
- 동일한 'CreateIoCompletionPort' 함수로 디바이스 핸들과 Completion Key를 바인딩
예) Socket 연결 ▼
CreateIoCompletionPort((HANDLE)socket, hIOCP, (ULONG_PTR)completionKey, 0);
3. 비동기 I/O 요청
- Overlapped I/O를 사용하여 비동기 작업을 요청
- 여기서 'OVERLAPPED' 구조체 사용
예) 데이터 송수신할 때
WSASend(socket, &wsaBuf, 1, &bytesSent, 0, &overlapped, NULL);
4. 작업 완료 통지
- 비동기 작업이 완료되면 IOCP는 해당 작업 정보를 내부 큐(Completion Queue)에 저장
- 이 큐에서 대기 중인 스레드가 작업 정보를 가져감
예) ▼
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED pOverlapped;
GetQueuedCompletionStatus(hIOCP, &bytesTransferred, &completionKey, &pOverlapped, INFINITE);

○ IOCP 내부 구조 정리
1) IOCompletionQueue : FIFO 큐로서 완료된 입출력 작업이 저장.
→ 대기 중인 스레드가 이 큐에서 작업을 가져옴
2) WaitingThreadQueue : LIFO 큐로 대기 중인 스레드가 저장.
→ 새로운 작업이 도착하면 이 큐에서 스레드를 꺼내 사용
3) ReleaseThreadList : 현재 실행 중인 스레드 목록.
→ 작업이 끝난 스레드는 다시 WaitingThreadQueue로 돌아감
4) PausedThreadList : 일시정지된 스레드가 저장.
→ ReleaseThreadList에 여유공간이 생길 때까지 대기
Echo Server 간단 구현
1. 서버 초기화
- Socket 생성하고 Binding 및 Listen 설정을 수행
- IOCP 오브젝트 생성하고 Listen 소켓을 IOCP에 등록
// IOCP 기반 Echo Server의 주요 흐름
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // IOCP 생성
2. 클라이언트 연결 처리
- 클라이언트 연결 요청을 수락(Accept)한 뒤 생성된 클라이언트 소켓을 IOCP에 등록
// 클라이언트 소켓 등록
CreateIoCompletionPort((HANDLE)clientSocket, hIOCP, (ULONG_PTR)clientKey, 0);
3. 데이터 송수신
- 클라이언트로부터 데이터를 비동기로 수신('WSARecv')하고,
수신된 데이터를 다시 클라이언트에게 송신('WSASend')
// 데이터 수신 요청
WSARecv(clientSocket, &recvBuffer, 1, &bytesReceived, &flags, &overlappedRecv, NULL);
4. 작업 완료 처리
- 'GetQueuedCompletionStatus'로 입출력 완료 정보를 가져와 적절히 처리하고 다음 작업 요청
// 완료 통지 대기 및 처리
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED pOverlapped;
while (true) {
BOOL result = GetQueuedCompletionStatus(hIOCP, &bytesTransferred, &completionKey,
&pOverlapped, INFINITE);
if (result && bytesTransferred > 0) {
// Echo 처리: 받은 데이터를 다시 송신
WSASend(clientSocket, &sendBuffer, 1, &bytesSent,
flags, pOverlappedSend, NULL);
}
}
5. 전체 흐름
// IOCP 기반 Echo Server의 주요 흐름
HANDLE hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); // IOCP 생성
// 클라이언트 소켓 등록
CreateIoCompletionPort((HANDLE)clientSocket, hIOCP, (ULONG_PTR)clientKey, 0);
// 데이터 수신 요청
WSARecv(clientSocket, &recvBuffer, 1, &bytesReceived, &flags, &overlappedRecv, NULL);
// 완료 통지 대기 및 처리
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED pOverlapped;
while (true) {
BOOL result = GetQueuedCompletionStatus(hIOCP, &bytesTransferred, &completionKey,
&pOverlapped, INFINITE);
if (result && bytesTransferred > 0) {
// Echo 처리: 받은 데이터를 다시 송신
WSASend(clientSocket, &sendBuffer, 1, &bytesSent,
flags, pOverlappedSend, NULL);
}
}
IOCP란 뭐다?
- IOCP는 Overlapped I/O와 결합하여 높은 성능과 확장성을 제공하는 Windows의 비동기 I/O 모델이다.
- 다수의 클라이언트를 효율적으로 처리하며, 네트워크 서버와 같은 고성능 애플리케이션에 적합하다.