1. Blocking vs Non-Blocking 소켓
1.1. Blocking 소켓
기존의 소켓은 기본적으로 Blocking 방식으로 동작한다.
특징:
- accept(), connect(), send(), recv() 등의 함수 호출 시 작업이 완료될 때까지 대기
- 작업이 완료되기 전까지는 다른 작업을 수행할 수 없음
- 네트워크 지연이 발생할 경우 전체 프로그램이 블로킹될 수 있음
1.2. Non-Blocking 소켓
Non-Blocking 소켓은 Blocking 방식의 단점을 해결하기 위해 도입되었다.
특징:
- 소켓 작업의 즉시 반환
- 다른 작업과 병행 처리 가능
- 프로그램의 반응성 향상
2. Non-Blocking 소켓의 동작 방식
즉시 반환 특성
- Non-Blocking 소켓은 작업의 성공/실패 여부와 관계없이 즉시 반환
- 작업이 완료되지 않았을 경우 WSAEWOULDBLOCK 에러 발생
- 반복적인 상태 확인이 필요
// Send
while (true)
{
// Non-Blocking에서는 SOCKET_ERROR가 꼭 문제상황이라고 볼 수 없음
if (::send(clientSocket, recvBuffer, recvLen, 0) == SOCKET_ERROR)
{
// WSAEWOULDBLOCK 상태면 아직 시도중인 상황
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
// Error
break;
}
cout << "Send Data! Len = " << recvLen << endl;
break;
}
3. 구현 예시
int main()
{
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
return 0;
u_long on = 1; // 1: non-blocking mode, 0: blocking mode
if (::ioctlsocket(listenSocket, FIONBIO, &on) == INVALID_SOCKET)
return 0;
// 서버 소켓 설정
// ...
SOCKADDR_IN clientAddr;
int32 addrLen = sizeof(clientAddr);
while (true)
{
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
// Error
break;
}
cout << "Client Connected" << endl;
// Recv
while (true)
{
char recvBuffer[1000];
int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
if (recvLen == SOCKET_ERROR)
{
if(::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
// Error
break;
}
else if (recvLen == 0)
{
// 연결끊김
break;
}
cout << "Recv Data Len = " << recvLen << endl;
// Send
while (true)
{
if (::send(clientSocket, recvBuffer, recvLen, 0) == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
// Error
break;
}
cout << "Send Data! Len = " << recvLen << endl;
break;
}
}
}
::WSACleanup();
}
4. Non-Blocking 소켓의 한계
4.1. Non-Blocking 방식의 문제점
- 지속적인 상태 확인으로 인한 CPU 자원 낭비
- 복잡한 에러 처리 로직 필요
- 실제로 Blocking 방식보다 더 많은 처리 오버헤드 발생
단순한 서버의 경우 Blocking 방식이 더 적합할 수 있다.
반복 확인으로 인한 오버헤드를 해결하기 위해 Select, Poll, Epoll 등의 모델을 사용한다.
'학습 > C++ 소켓 프로그래밍' 카테고리의 다른 글
IOCP (0) | 2025.03.30 |
---|---|
Overlapped I/O (0) | 2025.03.30 |
소켓 옵션 (0) | 2024.11.08 |
UDP 소켓 프로그래밍 (0) | 2024.10.31 |
TCP 소켓 프로그래밍 (0) | 2024.10.31 |