1. Select 모델
개념
Select 모델은 소켓의 상태를 감시하여, 읽기, 쓰기 또는 예외 처리가 가능한 시점을 알려주는 동기식 멀티플렉싱 모델입니다. 이는 단일 쓰레드에서 여러 개의 소켓을 동시에 감시하고, 준비된 소켓만 처리하는 방식입니다.
작동 원리
- 소켓 생성 및 초기화
- 소켓을 감시할 집합(fd_set)에 등록
select 함수 호출 → 소켓 상태를 감시 (읽기/쓰기/예외)
- 준비된 소켓만 반환 → 상태에 맞는 작업 수행 (예:
recv, send)
- 반복적으로 감시하며 루프 실행
Select 모델 주요 API
FD_ZERO(&set) : 소켓 집합을 초기화 (비움).
FD_SET(socket, &set) : 소켓을 소켓 집합에 등록.
FD_CLR(socket, &set) : 소켓을 소켓 집합에서 제거.
FD_ISSET(socket, &set) : 소켓이 준비 상태인지 확인.
select : 소켓 집합을 감시하여 준비된 소켓을 반환.
Select 모델 예제 코드 (서버)
fd_set readSet, writeSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
- 설명:
readSet과 writeSet은 소켓의 읽기/쓰기 상태를 감시하기 위한 소켓 집합입니다.
FD_ZERO를 사용하여 소켓 집합을 초기화합니다.
FD_SET(listenSocket, &readSet);
- 설명: 리스닝 소켓(서버 소켓)을 읽기 감시 집합에 등록합니다.
- 리스닝 소켓은 클라이언트의 연결 요청이 감지될 때 읽기 준비 상태가 됩니다.
for (Session& s : sessions) {
if (s.recvBytes <= s.sendBytes)
FD_SET(s.socket, &readSet);
else
FD_SET(s.socket, &writeSet);
}
- 설명:
- 모든 세션(클라이언트 소켓)을 반복하며, 소켓의 상태에 따라
readSet 또는 writeSet에 등록합니다.
recvBytes <= sendBytes:
- 데이터를 수신해야 하는 상태 → 읽기 감시 대상으로 등록.
recvBytes > sendBytes:
- 데이터를 송신해야 하는 상태 → 쓰기 감시 대상으로 등록.
int result = ::select(0, &readSet, &writeSet, nullptr, nullptr);
- 설명:
select 함수 호출로 소켓 상태 감시를 시작합니다.
- 인자:
0 : Windows에서는 사용되지 않음.
&readSet : 읽기 감시 대상 소켓 집합.
&writeSet : 쓰기 감시 대상 소켓 집합.
nullptr : 예외 소켓 감시는 하지 않음.
nullptr : 타임아웃 없이 무한 대기.
if (FD_ISSET(listenSocket, &readSet)) {
SOCKET clientSocket = ::accept(listenSocket, nullptr, nullptr);
sessions.push_back(Session{ clientSocket });
}
- 설명:
FD_ISSET를 통해 리스닝 소켓의 상태를 확인합니다.
- 준비 상태라면
accept를 호출하여 클라이언트 연결을 수락합니다.
- 새로 연결된 소켓을
sessions 벡터에 추가합니다.
for (Session& s : sessions) {
if (FD_ISSET(s.socket, &readSet)) {
int recvLen = ::recv(s.socket, s.recvBuffer, BUFSIZE, 0);
s.recvBytes = recvLen;
}
if (FD_ISSET(s.socket, &writeSet)) {
int sendLen = ::send(s.socket, &s.recvBuffer[s.sendBytes], s.recvBytes - s.sendBytes, 0);
s.sendBytes += sendLen;
if (s.sendBytes == s.recvBytes) {
s.recvBytes = 0;
s.sendBytes = 0;
}
}
}
- 설명:
FD_ISSET(s.socket, &readSet):
- 해당 소켓에서 읽기 가능한 상태라면
recv를 호출해 데이터를 수신합니다.
- 수신한 데이터의 크기를
recvBytes에 저장합니다.
FD_ISSET(s.socket, &writeSet):
- 해당 소켓에서 쓰기 가능한 상태라면
send를 호출해 데이터를 송신합니다.
- 송신이 완료되면
sendBytes를 갱신하고, 송수신 버퍼를 초기화합니다.
Select 모델 장단점
| 장점 | 단점 |
|---|
| 단일 쓰레드에서 여러 소켓 감시 가능 | 소켓 수 제한 (FD_SETSIZE) |
| 구현이 비교적 간단 | 감시 대상이 많아질수록 비효율적 |
| 다양한 OS에서 지원 | 매 루프마다 fd_set을 초기화해야 함 |
2. WSAEventSelect 모델
개념
WSAEventSelect 모델은 이벤트 기반 비동기 I/O 모델입니다. 소켓에 이벤트 객체를 연결하고, 특정 네트워크 이벤트(FD_READ, FD_WRITE 등)가 발생하면 이벤트 객체를 통해 이를 감지합니다.
작동 원리
- 이벤트 객체 생성 (
WSACreateEvent)
- 소켓과 이벤트 객체 연결 (
WSAEventSelect)
- 이벤트 감시 (
WSAWaitForMultipleEvents)
- 이벤트 발생 시 소켓과 상태를 확인 (
WSAEnumNetworkEvents)
- 적절한 소켓 함수 호출 (
accept, recv, send 등)
WSAEventSelect 주요 함수
WSACreateEvent() : 이벤트 객체 생성.
WSAEventSelect(socket, event, events) : 소켓과 이벤트 객체를 연결하고, 감지할 이벤트를 설정.
WSAWaitForMultipleEvents() : 이벤트 객체들의 상태를 감시.
WSAEnumNetworkEvents() : 발생한 네트워크 이벤트를 확인.
WSAEventSelect 모델 예제 코드
WSAEVENT listenEvent = ::WSACreateEvent();
WSAEventSelect(listenSocket, listenEvent, FD_ACCEPT);
- 설명:
- 이벤트 객체를 생성하고,
listenSocket과 연결합니다.
- 감시할 이벤트로
FD_ACCEPT를 지정 (클라이언트 연결 감지).
int32 index = ::WSAWaitForMultipleEvents(wsaEvents.size(), &wsaEvents[0], FALSE, WSA_INFINITE, FALSE);
index -= WSA_WAIT_EVENT_0;
- 설명:
WSAWaitForMultipleEvents를 호출하여 이벤트 발생을 감지합니다.
- 반환값은 발생한 이벤트 객체의 인덱스입니다.
WSANETWORKEVENTS networkEvents;
WSAEnumNetworkEvents(sessions[index].socket, wsaEvents[index], &networkEvents);
- 설명:
- 발생한 이벤트를
WSAEnumNetworkEvents를 통해 확인합니다.
networkEvents.lNetworkEvents에는 발생한 이벤트 비트 플래그가 설정됩니다.
if (networkEvents.lNetworkEvents & FD_ACCEPT) {
SOCKET clientSocket = ::accept(listenSocket, nullptr, nullptr);
WSAEVENT clientEvent = ::WSACreateEvent();
WSAEventSelect(clientSocket, clientEvent, FD_READ | FD_WRITE | FD_CLOSE);
}
- 설명:
FD_ACCEPT 이벤트가 발생하면 accept를 호출해 클라이언트 연결을 수락합니다.
- 새 클라이언트 소켓에 대해 이벤트 객체를 생성하고, 읽기/쓰기/종료 이벤트를 감지하도록 설정합니다.
WSAEventSelect 모델 장단점
| 장점 | 단점 |
|---|
| 이벤트 기반으로 효율적 | 이벤트 객체 생성 및 관리의 복잡성 |
| 다수의 소켓 감시에 적합 | Windows 전용 모델 |
| 논블로킹 소켓 자동 전환 | 커널 이벤트 객체 관리에 리소스 소모 |
Select vs WSAEventSelect 비교
| 항목 | Select | WSAEventSelect |
|---|
| 모델 | 동기식 멀티플렉싱 | 비동기 이벤트 기반 |
| 소켓 수 | FD_SETSIZE에 의해 제한 (기본 64개) | 이벤트 객체를 확장하여 무제한 가능 |
| 성능 | 소켓 수가 많을 경우 비효율적 | 대규모 소켓에 적합 |
| 호환성 | 다양한 플랫폼에서 사용 가능 | Windows 전용 |
| 복잡도 | 상대적으로 간단 | 이벤트 객체 생성 및 리셋 필요 |