IOCP를 공부하려고 인터넷을 검색하다가 종종 알고리즘에 뜨던 최홍배님의 영상을 보고 따라해보려고 한다.
https://www.youtube.com/watch?v=RMRsvll7hrM
IOCP 학습
먼저 IOCP가 뭔지 알아보자. IOCP라고 검색해보면 많은 글을 볼 수 있는데 정리하면 이렇다.
IOCP는 Input/Ouptput Completion Port의 약자로 소켓이나 파일의 입출력을 최소한의 스레드를 사용해서 처리하는 기법이다.
Overlapped I/O 방식에 스레드 풀링(Thread Pooling)과 큐(Queue) 메커니즘을 결합한 효율적인 비동기 처리 기술이다.
Overlapped I/O와 IOCP의 차이
일반 Overlapped I/O는 비동기 작업 완료 시 이벤트나 콜백을 통해 직접 알리는 방식입니다. 반면 IOCP는 작업 완료 시 즉시 알리지 않고, 커널의 IOCP Queue에 완료된 작업 결과를 저장한 후, Completion Port 객체를 통해 사용자에게 전달합니다.
이 과정에서 CreateIoCompletionPort() 함수로 소켓(또는 파일 핸들)과 컴플리션 키를 관리하는 별도의 Device List를 구성하여 효율성을 높입니다.
IOCP는 커널이 관리하는 스레드 풀을 통해 완료된 Overlapped I/O 작업을 Queue에서 꺼내 처리합니다.
IOCP 동작 흐름
IOCP 주요 함수
- CreateIoCompletionPort()
Completion Port 객체를 생성하며, 해당 객체에 소켓(또는 파일 핸들)과 사용자 정의 데이터(컴플리션 키)를 연결한다. - GetQueuedCompletionStatus()
Completion Port 객체를 통해 IOCP Queue에서 완료된 작업 결과를 가져온다. - PostQueuedCompletionStatus()
사용자가 직접 IOCP Queue에 임의 작업을 추가하는 함수로, 주로 서버 종료 시 대기 중인 스레드를 깨워 정상 종료를 유도할 때 사용한다.
IOCP Echo Server 구현하기
Echo Server를 구현하는게 1단계지만 아직 어려우니 코드 분석을 해보자.
enum class와 구조체
enum class IOOperation
{
RECV,
SEND
};
struct stOverlappedEx
{
WSAOVERLAPPED m_wsaOverlapped;
SOCKET m_socketClient;
WSABUF m_wsaBuf;
char m_szBuf[ MAX_SOCKBUF ];
IOOperation m_eOperation;
};
struct stClientInfo
{
SOCKET m_socketClient;
stOverlappedEx m_stRecvOverlappedEx;
stOverlappedEx m_stSendOverlappedEx;
stClientInfo()
{
ZeroMemory( &m_stRecvOverlappedEx , sizeof( stOverlappedEx ) );
ZeroMemory( &m_stSendOverlappedEx , sizeof( stOverlappedEx ) );
m_socketClient = INVALID_SOCKET;
}
};
- `enum class IOOperation`
- 비동기 I/O 작업의 종류를 구분하기 위한 열거형 클래스
- `RECV`
데이터 수신(Receive) 작업을 나타낸다. - `SEND`
데이터 송신(Send) 작업을 나타낸다. - `stOverlappedEx` 구조체 내에 이 열거형 변수를 두어, 완료된 I/O 작업을 식별하는 데 사용된다.
- `struct stOverlappedEx`
- Windows의 기본 `WSAOVERLAPPED` 구조체를 확장하여 비동기 I/O 작업에 필요한 추가 정보를 저장하는 구조체
- `WSAOVERLAPPED m_wsaOverlapped`
비동기 I/O 작업의 상태 정보를 저장하는 Windows 표준 구조체다. 운영체제가 이 구조체를 직접 사용한다. - `SOCKET m_socketClient`
이 I/O 작업이 발생한 클라이언트 소켓의 핸들이다. - `WSABUF m_wsaBuf`
데이터 버퍼의 위치와 크기를 담는 구조체다. `m_szBuf`를 가리키도록 설정해 사용한다. - `char m_szBuf[ MAX_SOCKBUF ]`
실제 데이터를 송수신하는 버퍼다. 크기는 `MAX_SOCKBUF`로 정의된 1024바이트다. - `IOOperation m_eOperation`
이 `OVERLAPPED` 작업이 `RECV`인지 `SEND`인지 구분하는 값이다.
- `struct stClientInfo`
- 서버에 접속한 클라이언트 한 명에 대한 모든 정보를 통합하여 관리하는 구조체
- `SOCKET m_socketClient`
해당 클라이언트와 연결된 소켓 핸들이다. - `stOverlappedEx m_stRecvOverlappedEx`
해당 클라이언트의 수신 작업을 위한 `stOverlappedEx` 구조체다. `WSARecv` 함수 호출 시 이 구조체가 사용된다. - `stOverlappedEx m_stSendOverlappedEx`
해당 클라이언트의 송신 작업을 위한 `stOverlappedEx` 구조체다. `WSASend` 함수 호출 시 이 구조체가 사용된다. - `stClientInfo()` : `stClientInfo` 구조체의 생성자
- `ZeroMemory(...)`
구조체가 생성될 때 내부에 있는 `stOverlappedEx` 멤버 변수들의 메모리 영역을 0으로 초기화 - `m_socketClient = INVALID_SOCKET`
소켓 핸들을 `INVALID_SOCKET`(-1)으로 초기화하여, 아직 유효한 클라이언트가 할당되지 않은 상태임을 명시
- `ZeroMemory(...)`
IOCP Echo Server 동작 흐름
- 소켓 초기화
- bind
- listen
- IOCP 오브젝트 생성
`Accept` 스레드에서
- 접속 승인 (클라이언트 소켓 생성)
- IOCP 오브젝트에 클라이언트 소켓 등록
- RECV 작업 요청
`Woker` 스레드에서
- RECV 작업 완료 (클라이언트에서 SEND)
- RECV 작업 후 수신 데이터 출력
- 클라이언트에 SEND 작업 수행 (Echo)
- 다시 RECV 작업 요청
- 1 - 4 과정을 반복
위의 과정을 코드로 구현하면 된다.
IOCompletionPort Class의 구현
- `InitSocket()` : `Listen` 소켓 생성
- `WSAStartup`, `WSASocket` 2가지 함수를 호출하여 소켓을 초기화 한다.
- `BindandListen()` : 클라이언트의 접속을 받기 위한 과정을 수행
- 서버의 주소정보를 `SOCKADDR_IN` 구조체에 기입
- `Listen` 소켓과 주소 정보를 `bind`
- 접속 요청을 받기 위해 `listen` 함수 호출
- `StartServer()` : IOCP 핸들을 생성하고 각 쓰레드를 활성화
- `CreateIoCompletionPort` : IOCP 핸들 생성
- WokerThread 활성화 - IO 작업 수행
- AccepterThread 활성화 - Accept 작업 수행
- `WokerThread`의 `main` : IO의 완료정보를 받아서 IO 작업을 수행. stOverlappedEx 구조체의 기입된 정보를 바탕으로 작업을 진행
- `GetQueuedCompletionStatus()` 함수로 IO의 완료정보를 받아온다.
- 만약 완료라면? `stOverlappedEx`의 `IOOperation` 값을 비교하여 이후 작업을 수행한다.
- `IOOperation`의 값이 `RECV`라면? `stOverlappedEx` 구조체의 버퍼를 확인하여 출력. 받은 버퍼를 그대로 송신(Echo)
- `IOOperation`의 값이 `SEND`라면? 송신한 내용을 출력
- `AccepterThread`의 `main` : 클라이언트 접속을 받는 작업 진행 후 IOCP 오브젝트에 클라이언트 소켓을 등록. 클라이언트와 통신을 진행할 준비를 마치는 작업
- `accept` 함수 호출
- 클라이언트가 접속 되었다면? IOCP 오브젝트에 클라이언트 소켓 등록
- `Recv Overlapped IO` 작업 요청
'C++' 카테고리의 다른 글
[단계별로 IOCP 실습] 5단계 효율적인 Send 구현 (1-Send 구현하기) (3) | 2025.08.06 |
---|---|
[단계별로 IOCP 실습] 4단계 네트워크와 로직 처리 스레드 분리 (4) | 2025.08.05 |
[단계별로 IOCP 실습] 3단계 애플리케이션과 네트워크 코드 분리 (2) | 2025.08.04 |
[단계별로 IOCP 실습] 2단계 메모리 최적화 (0) | 2025.08.01 |
공룡 점프 게임 만들기 (3) | 2025.07.30 |