P rogramming/Socket

9. 다중입출력 - select()

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


■ 다중 입출력 서버를 구현하기 위해 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 함수 사용이니 반드시 기억해야한다.