▶ TCP/IP select 함수 활용.
멀티 플렉싱을 구현하기위해서 Select 함수가 가장 많이 사용하고 있다. |
멀티플렉싱이란?
하나의 전송로를 여러 사용자가 동시에 사용해서 효율성을 극대화하는 것
I/O 멀티플렉싱이란?
- 클라이언트와 입/출력하는 프로세스를 하나로 묶어버리는 형식
- 프로세스가 고속의 전송로에 해당합니다.
아래와 같은 형식을
입/출력 프로세스를 하나로 묶어버립니다.
멀티 프로세스 VS 멀티플렉싱
- 멀티 프로세스 기반의 서버 ( Thread )
- 클라이언트와 서버간의 송수신 데이터의 용량이 큰 경우 적합
- 송수신이 쉬지않고 연속적으로 발생하는 경우 적합
- 멀티플렉싱 기반의 서버 ( Select )
- 클라이언트와 서버간의 송수신 데이터 용량이 작은 경우 적합
- 송수신이 연속적이지 않은 경우에 적합
- 멀티 프로세스 기반에 비해 많은 수의 클라이언트 처리에 적합
▶ Select() 함수에 대한 설명.
파일 디스크립터의 변화를 확인하는 함수
기본적으로 blocking 함수(확인할 파일 디스크립터에 변화가 생길 때까지 무한 대기)
멀티플렉싱 서버를 구현하기 위한 방법으로 select 함수가 가장 많이 사용되는 방법이고 윈도우즈 시스템에서도 동일한 이름으로 동일한
기능을 하는 함수를 제공하고 있으니 이식성에서도 높은 점수를 줄 수 있다
select 함수를 사용하게 되면, 한 곳에 모아놓은 여러 개의 파일 디스크립터를 동시에 관찰할 수 있다.
수신할 데이터를 지니고 있는 파일 디스크립터가 어떤 것들인지, 데이터를 전송할 경우 블로킹되지 않고 바로 전달 가능한 파일디스크립터는
어떤 것들인지, 그리고 예외가 발생한 파일 디스크립터는 어떤 것들인지 정도가 관찰 내용이 된다.
select 함수의 기능과 호출 순서
-- select 함수 사용 순서 --
1. 디스크립터 설정
2. 검사 범위 설정
3. 타임 아웃 설정
4. select 함수 호출
5. 결과 확인
-------------------------
===== 디스크립터 설정 =====
1) 파일 디스크립터 설정
- 변화를 확인할 파일 디스크립터들을 한 묶음으로 모아둔다.
- 파일 디스크립터를 모아두는 비트단위 자료형 fd_set 이용
- 3가지 변화(수신 데이터 존재유무, 데이터 송신 가능여부, 소켓의 예외상황 발생여부)별로 파일 디스크립터들을 구분지어 모아둠
FD_ZERO(fd_set * fdset); //fd_set 초기화 함수
FD_SET(int fd, fd_set * fdset); //해당 파일디스크립터 fd를 1로 셋
FD_CLR(int fd, fd_set * fdset); //해당 파일디스크립터 fd를 0으로 셋
FD_ISSET(int fd, fd_set * fdset); //해당 파일디스크립터 fd가 1인지 확인
===== 검사범위 설정 =====
2) 검사할 파일 디스크립터의 범위 지정
- 검사해야할 파일 디스크립터의 개수를 전달.
- 가장 큰 파일 디스크립터 값에 1을 더함(파일 디스크립터 값이 0부터 시작하므로)
===== 타임아웃 설정 =====
3) 타임 아웃 설정
- select 함수가 blocking되는 것을 피하기 위해 타임 아웃을 설정함.
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int n, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout);
리 턴 값 | 의 미 |
-1 | 오류 발생 |
0 | 타임 아웃 |
0보다 큰 수 | 변화발생 파일 디스크립터 수 |
- n : 검새 대상이 되는 파일 디스크립터의 수
- readfds : 이 리스트에 있는 식별자들은 시스템에 의해 즉시 입력이 가능한지 확인된다. 즉, 입력 가능으로 인해 반환된 식별자에 대해 recv()는 블로킹되지 않는다.
- writefds : 이 리스트에 있는 식별자들은 시스템에 의해 즉시 출력이 가능한지 확인된다, 즉 출력 가능으로 인해 반환된 식별자에 대해 send()는 블로킹되지 않는다.
- excepfds : 이 리스트에 있는 식별자들은 시스템에 의해 예상되는 예외 사항이나 에러가 발생했는지 확인된다. TCP소켓에서 발생할 수 있는 이러한 예상되는 예외 사항의 한 예로 상대방이 데이터 전송 중에 TCP연결을 끊었을 때가 있다. 이런 경우, 다음에 이어지는 읽기 또는 쓰기는 실패하고 ECONNRESET에러를 나타낸다.
- timeout : 함수 호출 후, 무한 대기 상태에 빠지지 않도록 타임-아웃(time-out)을 설정하기 위해
인자를 전달한다.
※ select 함수 호출시 전달되는 파일 디스크립터의 정보를 소켓뿐 아니라 파일을 나타내는 경우에도
전달 가능한다.
===== 결과 확인 =====
fd_set자료형에 파일디스크립터 0(stdin), 3의 변화를 확인하기 위해 설정
Select() 호출하면 변화가 생긴 파일디스크립터는 1로 세팅됨(그림에서 파일디스크립터 3이 변화가 발생한 것을 확인할 수 있음)
- 변화가 확인된 해당 파일디스크립터와 데이터 통신을 진행하면 된다.
[ 파일 디스크립터 범위 설정하기 ]
select 함수는 여러 파일 디스크립터를 검사하고, 그 결과를 전달해 준다.
select 함수는 여러 개의 파일 디스크립터를 확인해야 하는데, 이왕이면 확인해야 하는 파일 디스크립터의 범위를 제한해 주면, 보다 효율적으로 수행할 수 있다.
그래서 select 함수의 첫 번째 인자로 검사해야 하는 총 디스크립터의 개수를 넘겨주게 된다.
그러나 일반적으로 디스크립터는 생성될 때마다 값이 1씩 증가하기 때문에 가장 큰 파일 디스크립터 값에 1을 더해서 인자로 전달하면 된다.
1을 더하는 이유는 디스크립터 값이 0부터 시작하기 때문이다. 따라서 인자로 n이라는 값을 넘겨주게 되면 select 함수는 검사하게 되는 파일 디스크립터의 범위를 부터 n-1로 설정된다.
따라서 반드시 1을 더해줘야 한다.
[ 타임아웃 (time out) 설정하기 ]
타임아웃을 설정하기 위한 timeval 구조체
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
}
예를 들어서 tv_sec가 3이고 tv_usec가 500000 이면 타임아웃은 3.5초로 설정된다.
이렇게 설정한 timeval 구조체 변수의 포인터를 select 함수의 마지막 인자(timeout 인자)로 넘겨주게 되면 파일 디스크립터에 아무런 변화가 없더라도 3.5초가 지나면 무좋건 리턴하게 된다. 만약에 타임 아웃을 설정해 주지 않을 경우, NULL 포인터를 인자로 전달하면 된다.
[ select 함수 호출 이후 결과 확인 ]
select 함수가 리턴되고 나서 무엇보다도 중요한 것은 결과를 얻는 것이다. 일단 함수 호출이 정상적으로 리턴했다는 것은 파일 디스크립터에 변화가 있엇거나, 아니면 타임아웃이 발생했거나 둘 중에 하나이다.
리턴 값이 -1인 경우는 오류발생을 의미한다. 또한 0이 리턴 된 경우에는 타임아웃에 의해 리턴되었음을 의미한다. 즉 0이 리턴된 경우 파일 디스크립터에 아무런 변화도 발생하지 않았다는 의미가 된다.
그러나 리턴된 값이 0보다 큰 경우에는, 변화가 발생한 파일 디스크립터의 수를 의미하게 된다.
예를 들어 수신할 데이터가 존재하는 파일 디스크립터가 두 개 발생했다면, 2가 리턴될 것이다.
-------- select 호출 전 / 후 --------
==select 호출 전 ==
fd0 fd1 fd2 fd3 fd4
| 1 | 0 | 0 | 1 | 0 | 0 |...........................
==select 호출 후==
fd0 fd1 fd2 fd3 fd4
| 0 | 0 | 0 | 1 | 0 | 0 |...........................
------------------------------------------------- 예 제 소 스 ----------------------------------------------------
#include <stdio.h> int main() |
▶ 예제 소스
: 결과 출력. fd_set 의 크기 ( 128byte ) 와 FD_SET 으로 모니터 , 키보드의 변화를 감지하면 if문을 통해서 감지되었음을 확인 할 수 있다.
'TCPIP' 카테고리의 다른 글
2013.06.26_IP헤더구조_ (0) | 2013.06.26 |
---|---|
2013.06.24_TCP/IP이론_ (0) | 2013.06.24 |
2013.06.19_select()활용_Client소켓늘려보기_ (0) | 2013.06.20 |
2013.06.13_TCP/IP_Socket설정하기_ (1) | 2013.06.13 |
2013.06.13_IP주소설정하기_ (0) | 2013.06.13 |