네트워크프로그래밍 12 IO멀티플렉싱

zh025700·2022년 5월 11일
0

네트워크 프로그래밍


12. IO 멀티플렉싱

IO 멀티플렉싱 기반의 서버

다중 접속 서버를 어떻게 구현하는가

멀티프로세스 서버의 단점과 대안

  • 새로운 프로세스를 생성할 때 마다 많은 자원이 소요된다.
    => IO멀티플렉싱 서버를 쓰자!

TCP서버의 단점

  • 클라이언트가 figget에서 차단되고, 읽을때 데이터가 누락될 수 있음
  • 데이터를 보내고 받는 것은 독립적이어야 한다.

멀티플렉싱이란?

  • 하나의 통신채널을 통해 둘 이상의 데이터를 전송하는 기술
  • 최소한의 요소만 사용해 최대한의 데이터를 전달하는 기술

마치 실 전화기 3개를 연결할 때 하나의 라인당 연결된 컵을 쓰지않고 하나의 실로 3개의 컵을 연결하는 것과 같다

멀티프로세스에선 여러개의 프로세스가 클라이언트와 통신했다. 그럼 하나의 프로세스로 여러개의 클라이언트들과 어떻게 통신할까?

select함수와 서버 구현

  • select 함수를 사용하면 멀티플렉싱 서버를 구현할 수 있다.

select 함수 기능, 호출 순서

select 함수로 관찰할 수 있는 항목

  • 수신한 데이터를 지니는 소켓이 존재하는가?
  • 블로킹되지 않고 데이터의 전송이 가능한 소켓은 뭔가?
  • 예외상황이 발생한 소켓은 뭔가?

기능

  • 커널에 여러 이벤트 중 하나가 발생할 때까지 대기하도록 지시
  • 특정 이벤트가 실행되었을 때, 특정 시간이 지났을 때 프로세스를 깨운다

호출 순서

  1. 파일디스크립터 설정, 검사 범위 지정, 타임 아웃 설정
  2. select 호출
  3. 호출 결과 확인

복잡하다..

파일 디스크립터 설정


select를 사용하면 여러개의 파일 디스크립터(소켓)를 동시에 관찰 할 수 있다.
=> 관찰하고자 하는 디스크립터를 항목에 따라 정해야한다.

fd_set을 통해 관찰하고자 하는 디스크립터를 정한다

fd_set

  • 0과 1로 이루어진 비트 배열
  • 비트가 1이면 관찰의 대상임을 뜻함
    • 어떻게 등록을 하는가
      • 매크로 함수를 이용해 이루어진다

매크로 함수

  • 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가 있다면 양수를 반환
    • select의 호출 결과를 확인하는 용도로도 사용

관찰 범위지정과 타임아웃의 설정

select함수


int select(int maxfd,
  fd_set* readset,
  fd_set* writeset,
  fd_set* exceptset,
  const struct timeval* timeout);
  • 타입아웃 시 0, 관심 파일디스크립터에 변화시 변화된 파일디스크립터 수 값, 실패시 -1 반환
  • maxfd: 검사 대상이 되는 파일디스크립터의 수
  • readset: fd_set에 수신된 데이터 존재여부에 관심있는 디스크립터 정보를 등록해 변수의 주소값 전달
  • writeset: 블로킹 없는 데이터 전송 가능여부에 관심있는 디스크립터를 등록해 변수의 주소값 전달
  • exceptset: 예외상황의 발생여부에 관심 있는 디스크립터 정보를 등록해 주소값 전달
  • timeout: 무한 블로킹에 빠지지 않도록 설정

readset, writeset, exceptset에 다루고싶은 디스크립터를 명시한다!!

그럼 select의 관찰 범위, 타임아웃은 어떻게 정할까?

관찰범위

소켓이 하나씩 생성될 수록 디스크립터가 늘어나니 제일 큰 디스크립터 값에서 +1 해주면 된다!(0부터 시작이니)

타임아웃 시간(timeval)

struct timeval{
  long tv_sec; //초
  long tv_usec //마이크로초
}
  • select은 원래 관찰 디스크립터에 변화가 생겨야 반환
    • 관찰 디스크립터에 변화가 안생긴다면??? 무한 블로킹 상태에 빠짐

블로킹을 막기 위해 타임아웃을 지정

  • timeval 구조체 값을 설정 후 select에 구조체 주소값을 넣어주면 된다.
  • 만약 타임아웃으로 select가 반환이 된다면 반환값으로 반환의 원인 확인 가능
    • 반환값이 0이면 타임아웃으로 인한 반환!
  • 타임아웃 설정하지 않으려면 변수 값에 NULL을 넣으면 됨
    • 계속 기다림
  • 즉시 체크를 하려면 timeval 값에 0을 설정

timeval 값 초기화에 대해

select함수가 실행 되고 난 후, timeval의 값은 초기화 되지 않는다.(흐른대로 남아있는다)
그래서 select을 호출하기 전에 매번 timeval을 초기화해야한다.

select 함수 호출 이후의 결과 확인

0이 아닌 양수가 반환이 되면 그 수만큼 파일디스크립터의 변화가 생겼다는 것을 뜻함

  • select 함수에서 관찰 대상 파일 디스크립터에 해당 관심과 관련된 변화가 생겼다는 것을 의미

select가 실행되면 변화가 있었던 파일디스크립터 말고 다른 디스크립터는 0으로 초기화된다.

  • 그래서 보통 원본 유지를 위해 복제할 fd_set을 둬서 작업을 한다.
  • 이를 통해 변화가 있었던 파일 디스크립터를 확인할 수 있다
    • 변화가 없으면 fd_set에서 값이 0으로 바뀌니 변화 여부를 확인 가능하다

멀티플렉싱 서버

  • 서버 소켓은 항상 변화를 감지해야한다
    • 클라이언트의 연결 요청을 받아야 함
    • read set에 항상 있어야 한다
  • 클라이언트가 연결이 되면, 클라이언트 소켓은 클라이언트 배열에 들어간다
    • readset에 추가를 한다
      • 클라이언트가 데이터를 보내면 select이 값을 반환한다
        • 매크로 함수를 통해 어떤 클라이언트인지 확인 후 데이터를 읽는다
  • 클라이언트와 연결 종료가 되면, readset에서 값을 0으로 변경한다

단, 어떠한 파일 디스크립터에 변화가 있는지 확인을 위해
전체 파일디스크립터를 반복을 통해, 어떤 디스크립터에 변화가 있었는지 확인한다

profile
정리

0개의 댓글