멀티플렉싱(multiplexing)>>> 통신분야에서는 (다중화기)

: 지금까지 작성했던 프로그램들은 모두 하나의 단일 채널에서 일어는 입/출력만을 다루었습니다 즉 이전의 모든 버전의 에코 서버는 한번에 하나의 클라이언트 연결만을 처리 하였습니다 하지만 응용프로그램은 여러 채널의 입/출력을 동시에 처리하는 능력을 요구할 때가 자주 있습니다. 예를 들어 동시에 여러 포트를 열어서 에코서버를 할때 서버가 각 소켓을 생성하고 이를 각 포트에 바인딩 한 후 무슨 일이 일어날까를 생각해 봅시다. 기존방식에는 문제점이 발견되는데 서버는 연결을 accept할 준비가 되어있습니다 하지만 어떤 소켓을 선택해야 할지 선택을 하지 못합니다. 아무 서버나 연결하게 된다면 기존의 대기하고 있는 소켓역시 대기가 되어버리는 불편한 상황이 발생합니다. 물론 이러한 문제는 non-bloaking소켓을 이용하여 해결이 가능하지만 그것보다는 특정 소켓의 입/출력이 준비가 될때까지 서버를 bloacking을 하는 것이 좋습니다

 

하지만 단점도 있습니다 정리하자면

: 프로세스 생성에 많은 양의 연산, 메모리 공간 요구. IPC (inner process communcation) 방법도 복잡합니다

 


이런한 일련의 과정들은 가능하게 하는 것이
linux상에서 다시 말하면 unix상에서 제공해주는 select를 사용하면 됩니다 select는 입/출력이 예상되는 소켓의 식별자를 리스트로 명시하고 리스트의 식별자중 준비되어진 식별자를 준비가 되었는지를 반환하여 알리고 blocking이 되지 않을 것이라는 것을 확신하고 진행하게 됩니다

 

 

전반적인 순서는 아래와 같습니다

- 하나의 서버에 여러개의 클라이언트 제어
-
서비스 품질은 멀티프로세스보다 더 떨어지는것 같아보입니다
-
실질적으로 서버와 클라이언트의 통신에서 데이타 송수신은 매우 작다
-
그렇기 때문에 많은 클라이언트에게 서비스 할 수 있습니다.

- 하나의 프로세스가 해당 클라이언트n개 즉...파일 디스크립터를 묶어서
-
관리를 합니다
  
※ 즉 fd_set으로 묶어서 파일 디스크립터정보를 담습니다

 





select()함수>>>
select
함수를 사용하기 위해서 간단한 순서를 확인해보자

참고>>그림


----- select 함수 사용 순서 -------

1. 디스크립터 설정

2. 검사 범위 설정

3. 타임 아웃 설정

4. select 함수 호출

5. 결과 확인

------------------------------

 

 
 

1. 디스크립터 설정


1)
파일 디스크립트 설정

 

fd_set 수형;

0

1

0

1

.........

fd0  fd1  fd2  fd3

 

fd0 : stdin    fd1 : stdout    fd2 : stderr    fd3 : socket

 

변화를 확인할 파일 디스크립터들을 한 묶음으로 모아둔다

 

fd_set 자료형 관련함수

  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 되는 것을 피하기 위해 타임 아웃을 설정함

 

2. select함수 호출

 
헤더파일

#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);

                                                                                                  리턴값 : 성공시 0  이상... 오류 발생시 -1 리턴
                                                                                 0을 리턴하는 경우에는 타임아웃에 의해 리턴되었음을 의미
                                                                                 0 보다 큰경우는 변경된 파일 디스크립터의 수를 의미한다. 

인자 값 분석>>


n :
검색 대상이 되는 파일 디스크립터의 수

 

readfds : "입력스트림에 변화가 발생했는지" 확인하고자 하는 소켓들의 정보를 전달합니다. 여기서 입력 스트림에 변화가 발생했다는 것은 수신할 데이터가 있다는 뜻

 

writefds : "데이터 전송 시, 블로킹되지 않고 바로 전송이 가능한지" 확인하고자 하는 소켓들의 정보를 전달
 

excepfds : "예외가 발생했는지" 확인하고자 하는 소켓들의 정보를 전달
 

timeout : 함수 호출 후, 무한 대기 상태에 빠지지 않도록 타임-아웃(time-out)을 설정하기 위해 인자를 전달

 

예제>>

select(1,&reads,0,0,5)

<감지>5초동안 감지 수신에 변화가 없는지 감지하고 나와라 리턴값 = 1-

검사 디스크립트 수는 1


위 예제의 select같이 동작하는 프로그램을 작성합니다
 

select.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>

#define BUFSIZE 30

int main(int argc, char *argv[])
{
  fd_set reads, temps;
  int result;

  char message[BUFSIZE];
  int str_len;
  struct timeval timeout;

  FD_ZERO(&reads); //0
으로 초기화
  FD_SET(0,&reads); //
파일 디스크립터 0(stdin) 설정

  /*
  timeout.tv_sec 
= 5;
  timeout.tv_usec 
= 100000;
  */
  //
잘못된 timeout 설정(구조체 내에서만 업데이트된다)
  
  while(1)
  {
    temps = reads;
    
    timeout.tv_sec = 5//
실행후 다시 재설정해야한다
    timeout.tv_usec = 0;
    
    result = select(1&temps, 00&timeout);
    if(result == -1)
    {
      puts("select(): 
오류발생");
      exit(1);
    }
    else if(result == 0)
    {
      puts("select():
시간이 초과 되었습니다. ");
    }
    else
    {
      if(FD_ISSET(0,&temps))
      {
        str_len = read(0, message, BUFSIZE);
        message[str_len] = 0;
        fputs(message, stdout);
      }
    }
  }
  return 0;
}

 

결과>> 5초간 입력이 없을 경우 시간이 초과되었다는 경고 메시지가 출력됩니다

 

분석해보기>>
 
1)FD_ZERO(&reads); //0으로 초기화
    FD_SET(0,&reads); //
파일 디스크립터 0(stdin) 설정

1

0

0

0

.........

 

마지막 파일 디스크립터에 +1되게 되어있다?

 

1

0

0

1

.........

 

result = select(1&temps, 00&timeout);

수신데이터가 있는지 확인합니다 <5초설정>

확인할 파일디스크립트는 1개입니다

입력이 되었다면 result = 1



2)else
 if(result == 0) 5
초간 아무 변화가 없을때

 

0

0

0

0

.........


3)temps 
= reads;
원본값으로 초기화 시켜주질 않을 경우

 while문을 한바퀴 돌고나면

1

0

0

0

.........

에서

0

0

0

0

.........

으로 변화되고 이후로는 계속 0인 상태가 됩니다

 

select사용시에는 원본을 저장 할 수 있는 변수를 써야 합니다


3. 결과 확인


참고>>
select()함수의 인자를 보면 이 중 n은 검사가 필요없는 가장 작은 식별자값으로써 최대 식별자  값보다 1이 작다 위에서 보면 소켓셋에 담을 수 있는 소켓 디스크립터의 최대갯수는 시스템 정의 상수인 FD_SETSIZE로 정의 되어 있지만 그 수가 상당히 크므로 매번 그 크기만큼 검사하면 비효율적이므로 이를 효율적으로 검사하기 위해서 정수 n을 전달하여 그 크기+1까지만 검사한다

 그 다음에는 각 소켓셋(읽기셋, 쓰기셋, 예외셋)이 파라미터로 들어가며 만약 NULL이 들어가면 그 소켓셋은 대상 리스트에 대한 입/출력 감시를 하지 않는다
 마지막 파라미터인 timeout은 NULL로 설정하면 읽기셋, 쓰기셋, 예외셋에 삽입한 소켓 중에 변화가 생길 때까지 대기하고 있다가 변화가 발생한 소켓의 수를 리턴하게 된다.

timeout을 양수로 설정한 경우에는 변화가 발생한 소켓이 생길 때까지 대기하고 있다가 설정한 시간이 되면 변화가 발생한 소켓이 없더라도 대기상태를 해제하게 된다.


 이때 변화가 발생한 소켓이 없다면 0을 리턴하게 된다. 그리고 timeout 값이 0으로 설정되면 대기시간 없이 바로 리턴하게 된다


Posted by mantwo