#.멀티플렉싱 기반의 서버
멀티프로세스 서버의 단점과 대안
이전 Chapter에서는 다중접속 서버의 구현을 위해 클라이언트의 연결요청이 있을 때마다 새로운 프로세스를 생성하였다. 이는 실제 사용되는 방법이지만 문제가 전혀 없는 방법은 아니다. 프로세스의 생성에는 상당히 많은 대가를 지불해야 하기 때문이다. 많은 양의 연산이 요구되며, 필요한 메모리 공간도 비교적 큰 편이다. 또한 프로세스마다 별도의 메모리 공간을 유지하기 때문에 상호간에 데이터를 주고받으려면 다소 복잡한 방법을 택할 수밖에 없다.
프로세스의 생성을 동반하지 않으면서 다수의 클라이언트에게 서비스를 제공할 수 있는 방법으로 IO멀티플렉싱 서버가 있다. 하지만 이 모델은 구현하고자 하는 서버의 특성에 따라서 구현방법이 달리 결정되어야 한다. 즉 이 방법이 모든 경우에 있어서 최선은 아니다.
#.멀티플렉싱이라는 단어의 이해
• 하나의 통신 채널을 통해서 둘 이상의 데이터(시그널)를 전송하는데 사용되는 기술
• 물리적 장치의 효율성을 높이기 위해서 최소한의 물리적인 요소만 사용해서 최대한의 데이터를 전달하기 위해 사용되는 기술
• 멀티플렉싱의 개념을 서버에 적용하기
서버에 멀티플렉싱 기술을 도입해서 필요한 프로세스의 수를 줄일 수 있다.
• 위 모델에 멀티플렉싱 기술을 적용하면 다음과 같이 프로세스의 수가 줄어든다.
#.select 함수의 이해와 서버의 구현
select 함수의 기능과 호출 순서
select 함수를 사용하면 한곳에 여러 개의 파일 디스크립터를 모아놓고 동시에 이들을 관찰할 수 있다. 이때 관찰할 수 있는 항목은 다음과 같다.
• 수신한 데이터를 지니고 있는 소켓이 존재하는가?
• 블로킹되지 않고 데이터의 전송이 가능한 소켓은 무엇인가?
• 예외 상황이 발생한 소켓은 무엇인가?
select 함수의 호출방법과 순서는 다음과 같다.
#.파일 디스크립터의 설정
select 함수를 사용하면 여러 개의 파일 디스크립터를 동시에 관찰할 수 있다고 하였다. 물론 파일 디스크립터의 관찰은 소켓의 관찰로 해석할 수 있다. 그렇다면 먼저 관찰하고자 하는 파일 디스크립터를 모아야 한다. 모을 때도 관찰항목에 따라서 구분해서 모아야 한다.
파일 디스크립터를 세 묶음으로 모을 때 사용하는 것이 fd_set형 변수이다. 이는 다음 그림에서 보이듯이 0과 1로 표현되는, 비트단위로 이뤄진 배열이라고 생각하면 된다.
이 비트가 1로 설정되면 해당 파일 디스크립터가 관찰의 대상임을 의미한다. fd_set형 변수에 값을 등록하거나 변경하는 등의 작업은 다음 매크로 함수들의 도움을 통해 이뤄진다.
- FD_ZERO(fd_set* fdset)
인자로 전달된 주소의 fd_set형 변수의 모든 비트를 0으로 초기화
- FD_SET(int fd, fd_set* fdset)
매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보를 등록
- FD_CLR(int fd, fd_set* fdset)
매개변수 fdset으로 전달된 주소의 변수에서 매개변수 fd로 전달된 파일 디스크립터 정보를 삭제
- FD_ISSET(int fd, fd_set* fdset)
매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보가 있으면 양수를 반환
위의 함수들 중에서 FD_ISSET은 select 함수의 호출결과를 확인하는 용도로 사용된다.
#.검사(관찰)의 범위지정과 타임아웃의 설정
#include <sys/select.h>
#include <sys/time.h>
int select( int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);
- 성공 : 0 반환
- 실패 : -1 반환
• int maxfd
검사 대상이 되는 파일 디스크립터의 수
• fd_set* readset
fd_set형 변수에 '수신된 데이터의 존재여부'에 관심 있는 파일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달
• fd_set* writeset
fd_set형 변수에 ‘블로킹 없는 데이터 전송의 가능여부'에 관심 있는 파일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달
• fd_set* exceptset
fd_set형 변수에 '예외상황의 발생여부'에 관심이 있는 파일 디스크립터 정보를 모두 등록해서 그 변수의 주소 값을 전달
• const struct timeval* timeout
select 함수 호출 이후에 무한정 블로킹 상태에 빠지지 않도록 타임아웃(time-out)을 설정하기 위한 인자를 전달
• 반환 값
오류 발생시에는 -1 반환
타임 아웃에 의한 반환 시에는 0이 반환
관심 대상으로 등록된 파일 디스크립터에 해당 관심에 관련된 변화가 발생하면 0보다 큰 값이 반환되는데 이 값은 변화가 발생한 파일 디스크립터의 수를 의미한다
select 함수는 세가지 관찰항목의 변화를 확인하는데, 이 세가지 관찰항목별로 fd_set형 변수를 선언해서 파일 디스크립터 정보를 등록하고, 이 변수의 주소 값을 위 함수의 두 번째, 세 번째 그리고 네 번째 인자로 전달하게 된다. 그런데 이에 앞서 다음 두 가지를 먼저 결정해야 한다.
• 파일 디스크립터의 관찰(검사) 범위는 어떻게 되는지?
• select 함수의 타임아웃 시간을 어떻게 할지?
이 중 첫 번째 파일 디스크립터의 관찰(검사) 범위는 select 함수의 첫번째 매개변수와 관련이 있다. 따라서 fd_set형 변수에 등록된 파일 디스크립터의 수를 확인할 필요가 있는데 파일 디스킯터의 값은 생성될 때마다 1씩 증가하기 때문에 가장 큰 파일 디스크립터의 값에 1을 더해서 전달하면 된다.
두 번째 select 함수의 타임아웃 시간은 select 함수의 마지막 매개변수와 관련이 있는데 매개 변수 선언에 보이는 자료형 timeval 구조체 기반의 자료형으로 다음과 같이 정의되어 있다.
struct timeval
{
long tv_sec; // second
long tv_usec; // microseconds
}
select 함수는 관찰중인 파일 디스크립터에 변화가 생겨야 반환을 한다. 때문에 변화가 생기지 않으면 무한정 블로킹 상태에 머물게 된다. 바로 이러한 상황을 막기 위해서 타임아웃을 지정하는 것이다. 타임아웃을 설정하고 싶지 않을 경우 NULL을 인자로 전달하면 된다.
#.select 함수호출 이후의 결과확인
0이 아닌 양수가 반환이 되면 그 수만큼 파일 디스크립터에 변화가 발생했음을 의미한다.
select 함수가 양의 정수를 반환한 경우, 변화가 발생한 파일 디스크립터는 어떻게 알아낼 수 있을까? select 함수의 두 번째, 세 번째 그리고 네 번째 인자로 전달된 fd_set형 변수에 다음 그림에서 보이는 변화가 발생하기 때문에 어렵지 않게 알아낼 수 있다.
select 함수 호출이 완료되고 나면 select 함수의 인자로 전달된 fd_set형 변수에 변화가 생긴다. 1로 설정된 모든 비트가 다 0으로 변경되지만 변화가 발생한 파일 디스크립터에 해당하는 비트만 그대로 1로 남아있게 된다. 때문에 1로 남아있는 위치의 파일 디스크립터에서 변화가 발생했다고 판단할 수 있다.
select 함수를 호출하는 예제의 확인
결과
#.멀티플렉싱 서버의 구현
echo_selectserv.c
client.c
결과
[출처] : 윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어
'컴퓨터 기초 > TCP&IP' 카테고리의 다른 글
14.멀티캐스트 & 브로드캐스트 (0) | 2020.08.18 |
---|---|
13.다양한 입출력 함수들 (0) | 2020.08.18 |
11.프로세스간 통신 (0) | 2020.08.14 |
10.멀티프로세스 기반의 서버구현 (0) | 2020.08.13 |
9.소켓의 다양한 옵션 (0) | 2020.08.12 |