[TCP/IP 소켓 프로그래밍] (11-2) 다중 접속 서버 - 레벨 트리거/엣지 트리거
출처 : 열혈 TCP/IP 소켓 프로그래밍 윤성우 저
epoll 기반 이벤트 발생
이벤트 발생 감지(트리거)에 대한 기준을 두가지로 변경할 수 있다.
1. 레벨 트리거: 입력버퍼에 데이터가 남아있는 동안에 계속해서 이벤트가 등록된다.
2. 엣지 트리거: 입력버퍼에 데이터가 처음 수신될 때 이벤트가 등록된다.
기본 트리거 방식은 레벨 트리거이다.
- 엣지 트리거 설정
struct epoll_event;
event.events = EPOLLIN(수신 데이터 감지) | EPOLLET(엣지 트리거);
- 소켓 속성 변경
엣지 트리거 방식의 특성상 블로킹 방식으로 동작하는 read & write 함수의 호출은 서버를 오랜 시간 멈추는 상황으로 이어질 수 있다. 엣지 트리거 방식에서는 소켓을 논블로킹 모드에서 read & write 함수를 호출해야한다.
다음 함수를 이용하여 소켓 설정을 변경한다.
int fcntl(int filedes, int cmd, ...);
- 특성 변경의 대상이 되는 파일의 디스크립터 전달
- 함수 호출의 목적에 해당하는 정보 전달
int flag = fcntl(fd, F_GETFL, 0); // 기존 속성 정보
fcntl(fd, F_SETFL, flag|O_NONBLOCK); // 논블로킹 속성 추가
epoll 서버 - 엣지 트리거 버전
레벨 트리거 버전의 코드에서 클라이언트에게 에코 메시지를 전송하는 부분에서 다른 점이 있다.
기본 데이터 수신은 4바이트씩 읽는다. BUF_SIZE 4;
수신 버퍼가 더 이상 읽을 데이터가 없을 경우
read 결과로 에러(-1)이 반환되 었을 때, #include <errno.h>의 errno가 EAGAIN이 되는 상황, 수신 버퍼에 더이상 읽을 데이터가 없음을 의미한다.
// read all data in buffer
while(1)
{
// read 4 bytes
str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);
if(str_len == 0) // close request
{
// remove registration
epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
close(ep_events[i].data.fd);
printf("closed client: %d \n", ep_events[i].data.fd);
break;
}
else if(str_len < 0)
{
// empty buffer
if(errno==EAGAIN)
break;
}
else
{
write(ep_events[i].data.fd, buf, str_len);
}
}
엣지 트리거의 장점
데이터의 수신과 데이터가 처리되는 시점을 분리할 수 있다. 언제 데이터를 처리할 것인가? 종류와 로직에 따른 다른 구현이 필요할 때 유연한 서버 모델을 제공한다.