왜 Select가 등장했는가

논블로킹 단독의 한계

  • 논블로킹으로 바꾸면 블로킹은 사라지지만, 준비되지 않은 소켓을 계속 확인하는 busy polling 문제가 남습니다.
  • 연결 수가 늘수록 "아무 일도 없는 소켓 확인 비용"이 커져 CPU를 낭비합니다.

Select의 핵심 아이디어

  • "지금 읽기/쓰기 가능한 소켓만 알려달라"를 OS에 요청합니다.
  • 준비된 소켓에만 accept/recv/send를 수행해 불필요한 호출을 줄입니다.
  • 즉, 논블로킹에서 이벤트 기반으로 넘어가는 첫 단계입니다.

fd_setselect() 동작 규약

fd_set 기본

  • 관찰할 소켓 집합을 담는 구조입니다.
  • 매크로: FD_ZERO, FD_SET, FD_CLR, FD_ISSET

반환값 해석

select 반환값의미
> 0준비된 소켓 개수
0타임아웃(준비된 소켓 없음)
SOCKET_ERROR오류 (WSAGetLastError)

매우 중요한 규칙

  • select 호출 후 fd_set 내용은 준비된 소켓만 남도록 수정됩니다.
  • 따라서 다음 루프에서 재사용하면 안 되고, 매번 새로 구성(또는 master set 복사)해야 합니다.
  • Windows에서 select의 첫 번째 인자(nfds)는 무시됩니다.

Select 루프 기본 패턴

권장 코드 골격

fd_set masterRead;
FD_ZERO(&masterRead);
FD_SET(listenSock, &masterRead);
for (auto& s : sessions) FD_SET(s.socket, &masterRead);

while (running) {
    fd_set readSet = masterRead;          // 매 루프 복사
    timeval tv{};
    tv.tv_sec = 0;
    tv.tv_usec = 10000;                   // 10ms tick

    int ret = select(0, &readSet, nullptr, nullptr, &tv);
    if (ret == SOCKET_ERROR) {
        int err = WSAGetLastError();
        break;
    }
    if (ret == 0) {
        continue; // timeout
    }

    if (FD_ISSET(listenSock, &readSet)) {
        // accept 가능한 상태
    }
    for (auto& s : sessions) {
        if (FD_ISSET(s.socket, &readSet)) {
            // recv 처리
        }
    }
}

해석 포인트

  • 리스너 소켓이 read-ready면 accept 가능 신호입니다.
  • 연결 소켓이 read-ready면 데이터 수신 가능 또는 연결 종료 상태일 수 있습니다.
  • select가 준비를 보장해도 recv 결과는 >0/0/SOCKET_ERROR로 다시 분기해야 합니다.

read/write/except 집합 이해

집합의미대표 사용
readfds읽기 가능accept, recv
writefds쓰기 가능비동기 connect 완료 확인, send 가능 시점
exceptfds예외 상태OOB/오류 감지(Windows에서는 주로 오류 확인 보조)

실무 팁

  • 처음에는 readfds 중심으로 구현하고, 필요할 때 writefds를 확장합니다.
  • 논블로킹 connect를 쓸 때는 writefds 확인이 중요합니다.

성능 한계와 규모 확장 문제

구조적 한계

  • fd_set 크기 제한(Windows 기본 FD_SETSIZE=64)이 있습니다.
  • 매 루프마다 집합 재구성 + 선형 순회(O(n)) 비용이 듭니다.
  • 연결 수가 많을수록 유휴 소켓 검사 비용이 커집니다.

대응 방향

규모권장 모델
소규모/학습select
중간 규모WSAEventSelect/poll 계열
대규모 동접IOCP (Windows), epoll/kqueue (타 OS)

결론

  • Select는 "최종 해법"보다 "이벤트 기반 사고를 익히는 교재용/입문용 모델"로 보는 것이 정확합니다.

강의 시 유의사항

강조 포인트

  • Select의 본질은 Reactor 패턴의 첫 구현입니다.
  • fd_set이 호출 후 변경된다는 규칙을 반드시 실습으로 확인시키세요.
  • "Select를 왜 버리고 IOCP로 가는지"를 성능 관점으로 연결하세요.

자주 하는 오해

오해바로잡기
논블로킹이면 select 불필요busy polling 비용 때문에 필요
select 한 번 후 fd_set 재사용 가능호출 후 내용이 바뀌므로 재구성 필요
read-ready면 무조건 recv 성공종료/오류 상태도 포함 가능

체크 질문 (스스로 답해보기)

  • master setworking set을 분리해야 하는가?
  • select가 0을 반환했을 때 서버 루프는 어떻게 동작해야 하는가?
  • Select의 구조적 한계가 IOCP 도입으로 이어지는 이유는 무엇인가?

profile
李家네_공부방

0개의 댓글