소켓 입출력 모델 개요

bolee·2022년 5월 2일
0

소켓 입출력 모델(socket I/O model)다수의 소켓을 관리하고 소켓에 대한 입출력을 처리하는 일관된 방식을 뜻한다.

소켓 입출력 모델을 사용하면 프로그래밍 복잡도는 높아지지만 시스템 자원을 적게 사용하면서도 다수의 클라이언트를 효율적으로 처리하는 서버를 만들 수 있다. 여기에서는 소켓 입출력 모델을 학습하기 위한 예비 지식으로 소켓의 동작 모드를 알아보고, 윈도우 운영체제가 제공하는 다양한 소켓 입출력 모델의 종류와 특징을 개략적으로 살펴볼 것이다.

소켓 모드의 종류

소켓은 소켓 함수 호출 시 동작하는 방식에 따라 블로킹(blocking)과 넌블로킹(nonblocking) 소켓으로 구분하고 이를 소켓 모드(socket mode)라 부른다.

블로킹 소켓

블로킹 소켓(blocking socket)은 소켓 함수 호출 시 조건이 만족되지 않으면 함수가 리턴하지 않고 스레드 실행이 정지한다. 조건이 만족되면 소켓 함수가 리턴하면서 정지된 스레드가 깨어나 실행을 재개한다.

주요 소켓 함수와 리턴 조건을 요약하면 아래 표와 같다. 조건이 만족되지 않으면 소켓 함수가 리턴하지 않으므로, 응용 프로그램이 별도의 스레드를 사용하지 않는 한 다른 작업을 할 수 없다.

소켓 함수리턴 조건
accept()접속한 클라이언트가 있을 때
connect()서버에 접속을 성공했을 때
send(), sendto()응용 프로그램이 전송을 요청한 데이터를 소켓 송신 버퍼에 모두 복사했을 때
recv(), recvfrom()소켓 수신 버퍼에 도착한 데이터가 1바이트 이상 있고, 이를 응용 프로그램이 제공한 버퍼에 복사했을 때

SO_SNDTIMEO, SO_RECVTIMEO 소켓 옵션을 이용하면, 조건이 만족되지 않더라도 send(), sendto(), recv(), recvfrom() 함수가 일정 시간이 지나면 리턴하게 할 수 있다.

넌블로킹 소켓

넌블로킹 소켓(nonblocking socket)은 소켓 함수 호출 시 조건이 만족되지 않더라도 함수가 리턴하므로 스레드가 중단 없이 다음 코드를 수행한다. 예를 들면, 접속한 클라이언트가 없어도 accept() 함수가 리턴하고, 소켓 수신 버퍼에도 도착한 데이터가 없어도 recv() 함수가 리턴한다.

socket() 함수는 기본적으로 블로킹 소켓을 생성한다. 넌블로킹 소켓이 필요하면 다음과 같이 ioctlsocket() 함수를 호출해 소켓 모드를 변경해야 한다.

// 블로킹 소켓 생성
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
	err_quit("socket()");
   
// 넌블로킹 소켓 생성
u_long on = 1;
retval = ioctlsocket(sock, FIONBIO, &on);
if (retval == SOCKET_ERROR)
	err_quit("ioctlsocket()");

넌블로킹 소켓에 대해 소켓 함수를 호출할 때 조건이 만족되지 않으면 소켓 함수는 오류를 리턴한다. 이때는 WSAGetLastError() 함수를 이용해 오류 코드를 확인해야 한다. 대개 오류 코드는 WSAEWOULDBLOCK인데, 이는 조건이 만족되지 않았음을 나타내므로 나중에 다시 소켓 함수를 호출하면 된다.

넌블로킹 소켓 연습

아래 링크는 넌블로킹 소켓을 사용하여 구현한 TCP 서버이다.

https://github.com/LEEBONGHAK/TCP-IP_window_socket/tree/main/Chapter10/NonblockingTCPServer

실행 결과 블로킹 TCP서버와 같다. 다만, CPU 사용률은 차이가 크가 NonblockTCPServer 예제를 실행한 상태에서 Windows 작업 관리자를 띄워보면 CPU 사용률이 거의 100%에 가까운 것을 볼 수 있다. 이는 접속한 클라이언트가 없어도 accept()함수가 리턴하여 goto문에 의해 계속 수행되기 때문이다.

넌블로킹 소켓의 특징

넌블로킹 소켓의 특징을 정리하면 다음과 같다.

장/단점특징
장점- 소켓 함수 호출 시 항상 리턴하므로 조건이 만족되지 않아 스레드가 오랜 시간 정지하는 상황, 즉 교착 상태(deadlock)가 생기지 않는다.
- 멀티스레드를 사용하지 않고도 여러 소켓에 대해 돌아가면서 입출력을 처리할 수 있다. 필요하다면 중간에 소켓과 직접 관계가 없는 다른 작업을 할 수도 있다.
단점- 소켓 함수를 호출할 때마다 WSAEWOULDBLOCK과 같은 오류 코드를 확인하고 처리해야 하므로 프로그램 구조가 복잡해진다.
- 블로킹 소켓을 사용한 경우보다 CPU 사용률이 높다.

서버 작성 모델의 종류

소켓 입출력 모델에 기반한 서버를 작서하기 앞서 기존의 서버 작성 모델을 살펴볼 것이다. 소켓 입출력 모델을 기반하지 않은 일반적인 TCP 혹은 UDP 서버는 반복 서버와 병행 서버로 분류할 수 있다.

반복 서버

반복 서버(iterative server)는 여러 클라이언트를 한 번에 하나씩 처리한다. 스레드(thread) 1개만으로 구현된 서버로 다음과 같은 특징을 갖는다.

장/단점특징
장점- 스레드 1개만으로 구현하므로 시스템 자원 소모가 적다.
단점- 한 클라이언트의 처리 시간이 길어지면 다른 클아이언트의 대기 시간이 길어진다.

위의 특징 때문에 반복 서버는 UDP 서버를 작성할 때 적합하다. UDP는 TCP와 달리 논리적 연결 개념을 사용하지 않으므로, 반복 서버 방식으로 다수의 클라이언트를 동시에 서비스할 수 있다.

병행 서버

병행 서버(concurrent server)는 여러 클라이언트를 동시에 처리한다. 멀티스레드로 구현된 서버로 병행 서버의 특징은 다음과 같다.

장/단점특징
장점- 한 클라이언트의 처리 시간이 길어지더라도 다른 클라이언트에 영향을 주지 않는다.
단점- 스레드를 여러 개 생성하여 구현하므로 시스템 자원 소모가 많다.

이러한 특징 때문에 병행 서버는 TCP 서버를 작성할 때 적합하다. TCP는 UDP와 달리 논리적 연결 개념을 사용하므로 병행 서버 방식을 사용하지 않고는 다수의 클라이언트를 동시에 서비스하기 어렵다. 하지만 TCP 서버가 각 클라이언트와 아주 짧은 시간 동안만 통신하고, 동시에 접속하는 클라이언트 수가 제한되어 있다면 반복 서버 방식으로 작성해도 된다.

이상적인 소켓 입출력 모델의 특징

바람직한 소켓 입출력 모델은 반복 서버와 병행 서버의 장점을 모두 갖추면서 각각의 단점을 해결할 형태가 될 것이다. 유한한 자원(CPU, 메모리 등)을 갖춘 시스템에서 실행되는 서버가 제공해야 하는 이상적인 기능은 다음과 같다.

  • 가능한 많은 클라이언트가 접속할 수 있다.
  • 서버는 각 클라이언트의 서비스 요청에 빠르게 반응하며(short response time), 고속으로 데이터를 전송한다.(high throughput)
  • 시스템 자원 사용량(CPU 사용률, 메모리 사용량 등)을 최소화 한다.

이상적인 기능을 제공하는 서버를 구현하기 위해 소켓 입출력 모델에 요구되는 사항을 정리해보면 다음과 같다.

  • 소켓 함수 호출 시 블로킹을 최소화한다.
    블로킹을 없애려고 넌블로킹 소켓을 사용하는 것으로는 충분하지 않다. 함수 호출 시 조건이 만족되지 않아 그냥 리턴하게 되면 불필요한 CPU 사용률만 높아지기 때문이다. 이상적인 소켓 입출력 모델에서는 CPU 사용률을 최소로 하되 넌블로킹 소켓을 사용한 소켓 함수 호출이 항상 성공해야 한다. 소켓 함수 호출이 성공한다는 것은 조건이 만족되지 않아 WSAEWOULDBLOCK과 같은 오류 코드가 발생하는 경우가 없어야 한다는 뜻이다.
  • 스레드 개수를 일정 수준으로 유지한다.
    스레드를 사용하면 여러 작업이 동시에 처리되는 것처럼 보이지만, 실제로는 CPU가 여러 스레드를 짧은 시간 간격으로 번갈아 수행하는 것이다. 따라서 스레드 개수를 무작정 늘린다고 성능이 좋아지지는 않는다. 게다가 스레드는 자신만의 스택을 별도로 유지하므로 개수가 늘어날 경우 메모리 사용량이 많아지는 문제도 생긴다. 시스템에서 동시에 실행할 수 있는 스레드 개수는 CPU 개수와 일치하므로, 이상적인 소켓 입출력 모델에서는 CPU 개수에 비례하여 스레드를 생성하고 입출력을 처리할 수 있어야 한다.
  • CPU 명령 수행과 입출력 작업을 병행한다.
    하드웨어는 진정한 의미의 병렬 동작이 가능하므로 CPU 명령 수행(즉, 코드 실행)과 입출력 작업을 동시에 진행할 수 있다. 이상적인 소켓 입출력 모델에서는 CPU 명령 수행과 소켓 입출력 작업을 병행할 수 있어야 한다.
  • 유저 모드와 커널 모드 전환 횟수를 최소화한다.
    윈도우 운영체제에서 응용 프로그램은 유저 모드(user mode)와 커널 모드(kernel mode)를 끊임없이 오가며 실행된다. 여기서 문제는 유저 모드와 커널 모드 전환이 상당한 CPU 사이클(cycle)을 소모한다는 점이다. 이상적인 소켓 입출력 모델이서는 가능한 많은 작없을 모드 전환 없이 처리할 수 있어야 한다.

소켓 입출력 모델의 종류

윈도우 운영 체제에서 제공하는 소켓 입출력 모델은 아래 표와 같다.

소켓 입출력 모델윈도우 CE윈도우(클라이언트 버전)윈도우(서버 버전)
SelectCE 1.0 이상윈도우 95 이상윈도우 NT 이상
WSAAsyncSelectx윈도우 95 이상윈도우 NT 이상
WSAEventSelectCE .NET 4.0 이상윈도우 95 이상윈도우 NT 3.51 이상
OverlappedCE .NET 4.0 이상윈도우 95 이상윈도우 NT 3.511 이상
Completion Portx윈도우 NT 3.5 이상(윈도우 95/98/Me 제외)윈도우 NT 3.5 이상(윈도우 95/98/Me 제외)

참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018

0개의 댓글