Overlapped의 본질: 요청과 완료를 분리한다
핵심 사고 전환
recv/send는 "지금 실행해서 지금 결과 확인"에 가깝습니다.
WSARecv/WSASend(Overlapped)는 "지금 요청 등록, 완료는 나중에 통지"입니다.
- 즉, 호출 스레드가 오래 기다리지 않고 다음 일을 진행할 수 있습니다.
Reactor vs Proactor
| 관점 | Select/WSAEventSelect | Overlapped |
|---|
| 흐름 | 준비됨 감지 -> recv 호출 | WSARecv 먼저 등록 -> 완료 통지 |
| 패턴 | Reactor | Proactor |
| 핵심 질문 | "지금 읽을 수 있나?" | "등록한 I/O가 끝났나?" |
실무 핵심 한 줄
- Overlapped 서버는 "완료 이벤트 처리 + 다음 I/O 재등록" 루프입니다.
기본 호출 패턴과 반환값 해석
최소 요청 코드
DWORD flags = 0;
DWORD bytes = 0;
int ret = WSARecv(sock, &wsaBuf, 1, &bytes, &flags, &ov, nullptr);
반환값 해석 규칙
| 경우 | 의미 | 처리 |
|---|
ret == 0 | 즉시 완료 | bytes 즉시 처리 |
ret == SOCKET_ERROR + WSA_IO_PENDING | 정상(비동기 진행 중) | 완료 통지를 기다림 |
| 그 외 에러 | 실제 실패 | 로그 + 연결 종료/복구 |
자주 하는 실수
WSA_IO_PENDING을 실패로 처리하면 정상 요청을 끊어버리게 됩니다.
완료 통지 방식 3가지
방법 비교
| 방식 | 장점 | 한계/주의 |
|---|
이벤트(hEvent) | 동작이 직관적, 학습용 좋음 | 이벤트/핸들 관리 비용, 대규모 비효율 |
| 콜백(Completion Routine) | 코드 흐름을 콜백으로 구성 가능 | Alertable wait(SleepEx) 의존, 디버깅 난이도 |
| IOCP | 고성능, 대규모 확장성 | 초기 설계 난이도 높음 |
이벤트 방식 예시
WSAEVENT ev = WSACreateEvent();
ov.hEvent = ev;
int r = WSARecv(sock, &buf, 1, &bytes, &flags, &ov, nullptr);
if (r == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) {
}
WSAWaitForMultipleEvents(1, &ev, TRUE, WSA_INFINITE, FALSE);
WSAGetOverlappedResult(sock, &ov, &bytes, FALSE, &flags);
콜백 방식 핵심
- Completion Routine은 호출 스레드가 Alertable wait에 들어가야 실행됩니다.
- 실무 서버에서는 제어 복잡도 때문에 이벤트/IOCP를 더 많이 사용합니다.
버퍼와 OVERLAPPED 수명 규칙 (가장 중요)
절대 규칙
- I/O 완료 전까지
WSABUF의 메모리와 OVERLAPPED는 살아 있어야 합니다.
- 스택 버퍼/스택
OVERLAPPED를 등록 후 함수 종료하면 댕글링으로 크래시가 납니다.
동시 요청 규칙
- Outstanding I/O 하나당 OVERLAPPED 하나가 필요합니다.
- 같은
OVERLAPPED를 동시에 두 요청에 재사용하면 결과가 섞일 수 있습니다.
OverlappedEx 패턴
struct OverlappedEx {
OVERLAPPED ov{};
WSABUF wsaBuf{};
char buffer[8192];
int ioType = 0;
void* owner = nullptr;
};
- I/O 컨텍스트 구조체에 버퍼와 메타데이터를 함께 두면 수명 관리가 쉬워집니다.
- 이 패턴은 Part 18의 IOCP에서도 그대로 이어집니다.
완료 처리 루프에서 꼭 지킬 것
수신 완료 처리 기본
- 완료 바이트가
0이면 TCP graceful close로 보고 정리합니다.
> 0이면 패킷 파싱 후 다음 WSARecv를 재등록합니다.
송신 완료 처리 기본
- 한 번의 완료로 전체 송신이 끝난다고 가정하지 마세요.
- 남은 바이트가 있으면 다음
WSASend를 이어서 등록해야 안전합니다.
종료/취소 시나리오
- 소켓 종료 중에는 대기 중 I/O가 취소되어 완료가 에러로 도착할 수 있습니다.
- 종료 플래그를 두고 "늦게 도착한 완료"를 안전하게 무시/정리하는 정책이 필요합니다.
IOCP로 넘어가는 전환 기준
Overlapped만으로도 중요한 학습
- "요청 등록 -> 나중 완료 처리 -> 재등록" 구조를 익히면 IOCP 이해가 쉬워집니다.
전환이 필요한 신호
- 연결 수가 커지며 이벤트/핸들 관리가 부담이 될 때
- 완료 처리를 여러 워커에 분산해야 할 때
- 락 경쟁보다 완료 큐 기반 분배가 더 유리해질 때
결론
- Overlapped는 종착점이 아니라 IOCP로 가는 핵심 중간 단계입니다.
강의 시 유의사항
강조 포인트
WSA_IO_PENDING은 에러가 아니라 정상 진행 신호입니다.
- 수명 관리(버퍼/OVERLAPPED)는 성능보다 먼저 지켜야 하는 정합성 규칙입니다.
- 완료 처리 후 재등록을 빼먹으면 수신이 멈춥니다.
자주 하는 오해
| 오해 | 바로잡기 |
|---|
| Overlapped면 자동으로 고성능 서버 완성 | 완료 처리 설계/재등록/수명 관리가 핵심 |
WSA_IO_PENDING은 실패다 | 비동기 요청이 정상 등록된 상태 |
OVERLAPPED 하나를 계속 재사용해도 된다 | Outstanding I/O마다 별도 컨텍스트 필요 |
체크 질문 (스스로 답해보기)
WSARecv 호출 직후 어떤 경우가 정상 경로이고 어떤 경우가 실제 에러인가?
- 왜
OVERLAPPED와 버퍼를 스택에 두면 위험한가?
- 수신 완료 후 다음
WSARecv를 재등록하지 않으면 어떤 현상이 생기는가?