Non-Blocking 소켓

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