[게임 서버] 3.9 IOCP
카테고리: GameServer
태그: GameServer
📦 3. 소켓 프로그래밍
👉🏻 항목 9: IOCP
🎯 IOCP란?
정의:
- Overlapped I/O가 완료되면 감지해서 사용자에게 알려주는 역할을 함
- Overlapped I/O와 IOCP를 함께 사용하면 최고 성능을 낼 수 있음
- epoll에서는 I/O 입력이 가능하면 알려주었으므로, 서로 비슷함
💻 IOCP 기본 사용법
iocp = new iocp();
// (실제로는 CreateIoCompletionPort로 IOCP 핸들을 만든다)
foreach (s in sockets) {
iocp.add(s, GetUserPtr(s));
// 이 소켓의 Overlapped I/O 완료 통지는 이 IOCP로 오게 된다.
// GetUserPtr(s)는 나중에 이벤트에서 "누구(어떤 세션) 이벤트인지" 구분하는 completion key.
s.OverlappedReceive(data[s], receiveOverlapped[s]);
// (실제로는 WSARecv)
// data[s] 버퍼 포인터와, receiveOverlapped[s]의 주소(OVERLAPPED*)를 커널에 넘겨서
// "recv 요청"을 걸어 둔다.
// 완료되면 IOCP 이벤트에 overlappedPtr로 이 포인터가 그대로 돌아온다.
}
events = iocp.wait(100ms);
// (GetQueuedCompletionStatus(Ex))
// IOCP 큐에서 완료된 I/O들을 꺼낸다(최대 100ms 대기).
// 각 이벤트는 (completionKey=userPtr, overlappedPtr, bytes, error)를 포함한다.
foreach (event in events) {
userPtr = event.userPtr; // completion key (보통 Session*)
ov = event.overlappedPtr; // WSARecv에 넘긴 OVERLAPPED* 그대로
s = GetSocketFromUserPtr(userPtr);
if (ov == &receiveOverlapped[s]) { // "이 완료는 recv 완료다"를 구분
Process(s, userPtr, data[s]); // 실제론 event.bytes만큼 처리해야 함
s.OverlappedReceive(data[s], receiveOverlapped[s]);
// 다음 데이터를 받기 위해 recv를 다시 건다.
}
}
핵심 동작:
- IOCP 핸들 생성 (
CreateIoCompletionPort) - 각 소켓을 IOCP에 등록하고 completion key 설정
- Overlapped I/O 요청 (
WSARecv등) - 완료된 I/O 이벤트 대기 및 처리 (
GetQueuedCompletionStatus) - 이벤트 처리 후 다음 I/O 요청
🔌 IOCP Accept 처리
AcceptEx 처리 과정:
- 초기 설정:
- listen socket을 IOCP에 연결해두고,
AcceptEx를 Overlapped로 미리 걸어둠 - 새 연결이 들어올 때마다
AcceptEx완료 이벤트가 IOCP로 들어온다
- listen socket을 IOCP에 연결해두고,
- 완료 이벤트 처리:
- IOCP에서
AcceptEx완료 이벤트를 받으면, 그 이벤트의 OVERLAPPED로 요청 컨텍스트를 찾아 accept socket을 꺼냄 SO_UPDATE_ACCEPT_CONTEXT를 적용해 연결 소켓을 완성- 이후 이 accept socket을 IOCP에 연결하고 recv를 건다
- IOCP에서
- 다음 연결 대기:
- 다음 accept를 위해
AcceptEx를 다시 게시한다
- 다음 accept를 위해
🧵 스레드 풀 구현의 차이
IOCP:
- IOCP의 경우 스레드 풀을 쉽게 구현할 수 있다
epoll:
- epoll은 그렇지 않다
⚖️ epoll의 스레드 처리 문제
epoll의 한계:
- epoll은 I/O 여부와 관계없이 I/O 가능 이벤트가 온다
- 한 epoll에 대해 여러 스레드가 동시에 이벤트 발생을 기다리는 경우 문제가 있다
- 연동된 소켓 하나가 UDP 데이터 2개를 수신 큐에 가지고 있으면, epoll 이벤트는 같은 소켓에 대해 두 스레드에서 동시에 꺼낸다
- 데이터 순서는 알기 힘들다
✅ IOCP의 스레드 처리 장점
IOCP의 강점:
- 소켓에 대해 Overlapped I/O를 하지 않으면, 소켓에 대한 완료 신호가 발생하지 않는다
- 즉, 소켓 하나에 대한 완료 신호를 스레드 하나만 처리하게 보장할 수 있다
- IOCP 하나를 여러 스레드가 기다리게 구현하기 쉽다
📊 IOCP/epoll 장단점 비교
| 구분 | IOCP | epoll |
|---|---|---|
| 블로킹 없애는 수단 | Overlapped I/O | 논블록 소켓 |
| 블로킹 없는 처리 순서 | 1. Overlapped I/O 걸기 2. 완료 신호 꺼내기 3. 완료 신호에 대한 나머지 처리 4. 끝난 후, 다시 Overlapped I/O 걸기 |
1. I/O 이벤트 꺼내기 2. 꺼낸 이벤트에 대응하는 소켓에 대한 논블록 I/O 실행 |
| 지원 플랫폼 | 윈도우 | 리눅스, 안드로이드 |
| 스레드 풀 구현 | 쉬움 | 어려움 |
| 동시 처리 안정성 | 소켓당 하나의 스레드만 처리 보장 | 동시 처리 시 순서 문제 발생 가능 |
🧐 정리
IOCP의 핵심 특징:
- Overlapped I/O의 완료를 효율적으로 감지하고 처리한다
- completion key를 통해 세션/연결을 구분한다
- OVERLAPPED 구조체 포인터로 I/O 요청 타입을 구분한다
- 스레드 풀 구현이 용이하고 안정적이다
IOCP vs epoll:
- IOCP는 완료 기반, epoll은 준비 완료 기반
- IOCP는 Windows 전용, epoll은 Linux/Android 전용
- IOCP는 멀티스레드 환경에서 더 안정적이다
- 둘 다 고성능 서버 구현을 위한 필수 기술이다
사용 시나리오:
- Windows 서버: IOCP + Overlapped I/O
- Linux 서버: epoll + 논블록 소켓
- 크로스 플랫폼: 플랫폼별 추상화 레이어 구현 필요
댓글남기기