■ 다중 입출력 서버를 구현하기 위해 select() 함수로 파일디스크립터의 변화를 확인.

(1) select() - 다중 입출력
: 여러개의 파일 디스크립터를 동시에 관찰하고, 변화가 있을 시 해당 파일 디스크립터를 반납한다.

○ 관찰항목
- 수신할 데이터를 지니고 있는 파일 디스크립터가 있는지.

- 데이터를 전송할 경우 blocking(무한대기) 상태가 되지 않고 바로 전달 가능한 파일 디스크립터가 있는지.
- 예외가 발생한 파일 디스크립터가 있는지.

=> 위와 같이 관찰 항목이 세가지이므로 파일 디스크립터들은 각 특성에 따라 readfds, writefds, exceptfds에 지정된다.
    파일 디스크립터를 세 묶음으로 모아놓기 위해 하용되는 것이 fd_set 데이터 타입의 자료형이다.
    fd_set은 0과 1로 저장하는 비트들의 배열이다.

○ 함수원형
  int select((int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

- nfds : 검사 대상이 되는 fd 범위로 세개의 집합(readfds/writefds/exceptfds)중 가장 큰 파일 디스크립터 값에 1을 더해서 전달.
- readfds : 시스템에 의해 즉시 입력이 가능한지 확인(읽기가 block되지 않았는지 검사하기 위한 리스트/ EOF 발생 검사)
- writefds : 시스템에 의해 즉시 출력이 가능한지 확인(쓰기가 block되지 않았는지 검사하기 위한 리스트)
- exceptfds : 예외가 있는지 검사하기 위한 리스트
- timeout : select가 반환하기 전에 블럭킹될 수 있는 시간의 제한 값 (0은 즉시반환 / NULL은 무한 block)

○ 리턴값
  -1 : 오류발생, 0 : 타임아웃, 0보다 큰수 : 변화 발생 파일 디스크립터 수


(2) FD_SET
: FD_SET 구조체 / 변수 조작하는데 사용되는 함수들

○ FD_SET 구조체

 typedef struct fd_set {
    u_int          fd_count;
    SOCKET    fd_array[FD_SETSIZE];
 } fd_set;

- fd_count : 설정하는 파일 디스크립터의 번호
- fd_array : 설정된 파일 디스크립터의 배열
=> fd_set은 0과 1로 저장되는 비트들의 배열이다.

○ FD_SET 변수 조작 함수
  - FD_SETFD_ZERO(fd_set *fdset) : fdset 포인터가 가리키는 변수의 모든 비트들을 0으로 초기화
  - FD_SET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보 설정(1로 설정)
  - FD_CLR(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보를 삭제(0으로 설정)
  - FD_ISSET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수가 fd로 전달되는 파일 디스크립터의 정보를 지니는지 확인


(3) timeout
: select 함수 호출 후 blocking(무한대기) 상태에 빠지지 않도록 timeout 을 설정하기 위한 인자 전달

○ timeout 구조체

 struct timeval {
    long    tv_sec;
    long    tv_usec;
 }

-  tv_sec : 초 단위 설정 변수
- tv_usec : 마이크로 초 단위 설정 변수


(4) 결과확인
  : FD_SET 변수에 변화가 생긴 파일 디스크립터는 1로 세팅, 변화가 없는 파일 디스크립터는 0으로 세팅한다.

- select 함수 호출 전 관찰 대상으로 fd0 과 fd3 이 설정되어 있다.
- select 함수 호출 후 fd3만 1로 설정되어있다.
=> fd3에 변화가 생겼음을 확인 & 해당 파일 디스크립터와 데이터 통신을 진행한다.


(5) 다중 입출력 예제 소스

○ Source


○ Compile & Result


- FD_SET 으로 파일디스크립터 0번(표준입력) 설정
- 초기 표준입력이 없이 5초 후 타임아웃 발생 (select함수의 timeout인자 5초 설정)
- 파일디스크립터 0번(표준입력) 으로 welcome! 을 입력. (변화발생)
- fd_set 변수 reads에1 세팅.
- select 함수에서 변화확인하고, FD_ISSET으로 0번(표준입력)의 셋팅된 값 확인
- read 함수로 0번(표준입력)의 값 message에 저장
- message 값 출력

※ temp 변수의 사용
: 이미 준비해둔 fd_set 변수를 temp 변수에 복사해 두고 있다.  그 이유는 select 함수 호출이 끝나면 변화가 생긴 파일디스크립터 위치를 제외한 나머지 위치의 비트들이 0으로 초기화된다. 따라서 원본 변수를 직접 select 함수의 인자로 전달해 버리면 또 다시 변수를 설정하는 과정을 거쳐야 한다.
이를 막기 위해서 원본은 보존 하고 임시 변수에 원본을 복사해서 이것을 가지고 select 함수를 호출한다. 일반적인 select 함수 사용이니 반드시 기억해야한다.


by 민트앤라떼 2012. 3. 12. 15:24