등장 배경과 핵심 아이디어
왜 Select 다음에 배우는가
- Select는 매 루프마다
fd_set 재구성과 순회가 필요합니다.
- WSAEventSelect는 "소켓에 이벤트를 연결"해서 준비 상태를 더 직접적으로 기다립니다.
- 즉, Reactor 모델을 Windows 이벤트 객체 기반으로 구현한 형태입니다.
Select와 비교
| Select | WSAEventSelect |
|---|
fd_set 구성 후 select() | 소켓-이벤트 연동 후 WSAWaitForMultipleEvents() |
FD_ISSET으로 준비 확인 | WSAEnumNetworkEvents로 이벤트 종류 확인 |
| 집합 기반 폴링 성격 | 이벤트 신호 기반 대기 |
핵심 요약
- "준비됐는지 매번 확인"에서 "준비되면 깨워줌"으로 사고가 바뀝니다.
주요 API와 이벤트 종류
주요 API
| 함수 | 역할 |
|---|
WSACreateEvent() | 이벤트 객체 생성 |
WSAEventSelect(sock, ev, mask) | 소켓과 이벤트 객체 연동 |
WSAWaitForMultipleEvents() | 이벤트 배열 대기 |
WSAEnumNetworkEvents() | 실제 발생 이벤트 확인 + 내부 상태 정리 |
WSACloseEvent() | 이벤트 객체 해제 |
자주 쓰는 이벤트 마스크
| 이벤트 | 의미 |
|---|
FD_ACCEPT | 수락 가능한 연결 도착 |
FD_READ | 읽을 데이터 도착 |
FD_WRITE | 쓸 수 있는 상태 |
FD_CLOSE | 연결 종료 감지 |
FD_CONNECT | connect 완료/실패 |
기본 처리 흐름
초기화 패턴
WSAEVENT ev = WSACreateEvent();
WSAEventSelect(sock, ev, FD_READ | FD_CLOSE);
대기 루프 예시
DWORD idx = WSAWaitForMultipleEvents(eventCount, events,
FALSE, 1000, FALSE);
if (idx == WSA_WAIT_TIMEOUT) {
} else if (idx >= WSA_WAIT_EVENT_0 &&
idx < WSA_WAIT_EVENT_0 + eventCount) {
int i = static_cast<int>(idx - WSA_WAIT_EVENT_0);
WSANETWORKEVENTS ne{};
WSAEnumNetworkEvents(sockets[i], events[i], &ne);
if (ne.lNetworkEvents & FD_READ) { }
if (ne.lNetworkEvents & FD_CLOSE) { }
}
처리 순서 규칙
- 대기
- 어떤 이벤트가 깨어났는지 인덱스 확인
WSAEnumNetworkEvents로 실제 이벤트 플래그 확인
- 해당 I/O 처리
중요한 동작 규칙(실수 포인트)
자동 논블로킹 전환
WSAEventSelect를 호출하면 해당 소켓은 논블로킹 모드로 동작합니다.
- 따라서
recv/send에서 WSAEWOULDBLOCK 분기가 계속 필요합니다.
이벤트 리셋
WSAEnumNetworkEvents 호출 시 연결된 이벤트 객체 상태가 정리됩니다.
- 이 호출을 누락하면 같은 이벤트가 반복되거나 상태가 꼬일 수 있습니다.
이벤트 해제
- 소켓 종료 시
closesocket뿐 아니라 WSACloseEvent도 잊지 않아야 합니다.
- 이벤트 핸들 누수는 장기 실행 서버에서 치명적입니다.
성능 한계와 적용 범위
꼭 알아야 할 한계
- Windows 전용 모델입니다.
WSAWaitForMultipleEvents는 한 번에 기다릴 수 있는 이벤트 수가 제한됩니다.
- 일반적으로
WSA_MAXIMUM_WAIT_EVENTS(64) 제약 때문에 대규모 서버에는 부적합합니다.
실제 적용 권장
| 시나리오 | 권장 |
|---|
| 소켓 수가 적은 도구/클라이언트 | WSAEventSelect 충분 |
| 중간 규모 서버 프로토타입 | 가능하지만 관리 복잡도 증가 |
| 대규모 동접 게임 서버 | IOCP 권장 |
결론
- WSAEventSelect는 Select보다 이벤트 지향적이지만,
- 규모가 커지면 결국 IOCP 같은 완료 기반 모델로 넘어가는 징검다리입니다.
강의 시 유의사항
강조 포인트
- Select -> WSAEventSelect -> IOCP의 진화 흐름을 반드시 연결해 설명하세요.
- "이벤트 모델"이지만 여전히 논블로킹 분기 처리(
WOULDBLOCK)가 필요함을 강조하세요.
- 64 제한(대기 핸들 수)을 실무 한계로 명확히 짚으세요.
자주 하는 오해
| 오해 | 바로잡기 |
|---|
| WSAEventSelect는 소켓 수 제한이 없다 | 대기 이벤트 수 제한이 존재 |
| 이벤트가 오면 무조건 recv 성공 | FD_READ 후에도 분기/오류 처리가 필요 |
| 이벤트 객체는 소켓 닫으면 자동 정리 | WSACloseEvent로 별도 해제 필요 |
체크 질문 (스스로 답해보기)
- 왜
WSAEnumNetworkEvents 호출이 필수인가?
- WSAEventSelect가 소규모에는 충분하지만 대규모 서버에는 부적합한 이유는?
- WSAEventSelect 설정 후
recv 코드가 기존과 어떻게 달라져야 하는가?