728x90
레벨 트리거와 엣지 트리거의 차이
- 레벨 트리거 : 소켓 버퍼에 데이터가 남아있는 동안에 계속 이벤트 발생
- 엣지 트리거 : 소켓 버퍼에 데이터가 들어오는 순간에만 이벤트를 발생
-> 새로운 데이터가 들어와야만 이벤트 발생
- 기본적으론 레벨 트리거로 설정이 되어 있음
- 서버쪽에서 컨트롤 요소가 많고 데이터 송수신이 빈번한 경우는 엣지 트리거가 유리하고 단순하고 데이터 송수신 상황이 다양하지 않으면 레벨 트리거 방식이 유리
엣지 트리거 기반의 서버 구현
- 넌 블로킹 IO로 소켓을 변경
-> 엣지 트리거는 데이터 수신 시 한번만 이벤트가 발생되기 때문에 충분한 양의 버퍼를 마련한 다음 데이터를 읽어 들여야 함
-> 데이터의 양에 따라 블로킹이 발생할 수 있기 때문에 넌 블로킹 IO로 변경
-> 그리고 errno 변수를 참조해서 EAGAIN 값이면 버퍼가 빈 상태인 것을 확인
int flag=fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag|O_NONBLOCK);
- BUF_SIZE를 작게 해서 입력 버퍼에 데이터가 남아 있게 설정
#include BUF_SIZE 4
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t adr_sz;
int str_len, i;
char buf[BUF_SIZE];
struct epoll_event *ep_events;
struct epoll_event event;
int epfd, event_cnt;
...;
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
...;
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
printf("bind() error");
if(listen(serv_sock, 5)==-1)
printf("listen() error");
epfd=epoll_create(EPOLL_SIZE); // epoll 저장소 생성
ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE); // 이벤트 저장할 공간 생성
event.events=EPOLLIN;
event.data.fd=serv_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event); // 서버 소켓의 이벤트 등록
while(1)
{
event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); // 이벤트 발생 대기
if(event_cnt==-1)
{
puts("epoll_wait() error");
break;
}
for(i=0; i<event_cnt; i++)
{
if(ep_events[i].data.fd==serv_sock)
{
adr_sz=sizeof(clnt_adr);
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
event.events=EPOLLIN;
event.data.fd=clnt_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
// 새로운 소켓도 등록
printf("connected client: %d \n", clnt_sock);
} // 발생한 소켓이 서버 소켓이면
else
{
str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
if(str_len==0) // close request!
{
epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
// 소켓을 epoll 관리 대상에서 삭제
close(ep_events[i].data.fd); // 소켓 삭제
printf("closed client: %d \n", ep_events[i].data.fd);
}
else
{
write(ep_events[i].data.fd, buf, str_len); // echo!
}
}
}
}
close(serv_sock); // 서버 소켓 소멸
close(epfd); // epoll 인스턴스 소멸
return 0;
}
-> 레벨 트리거일 경우 이벤트가 계속 발생
-> 엣지 트리거일 경우 한번만 발생
int main(int argc, char *argv[])
{
...;
epfd=epoll_create(EPOLL_SIZE);
ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
int flag=fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag|O_NONBLOCK); // 넌 블로킹 모드
event.events=EPOLLIN;
event.data.fd=serv_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);
while(1)
{
event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);
if(event_cnt==-1)
{
puts("epoll_wait() error");
break;
}
puts("return epoll_wait");
for(i=0; i<event_cnt; i++)
{
if(ep_events[i].data.fd==serv_sock)
{
adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
int flag=fcntl(clnt_sock, F_GETFL, 0);
fcntl(clnt_sock, F_SETFL, flag|O_NONBLOCK); // 클라이언트 소켓도 넌 블로킹
event.events=EPOLLIN|EPOLLET;
event.data.fd=clnt_sock;
epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
printf("connected client: %d \n", clnt_sock);
}
else
{
while(1)
{
str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
if(str_len==0) // close request!
{
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)
{
if(errno==EAGAIN) // 버퍼가 비어있는지 계속 확인
break;
}
else
{
write(ep_events[i].data.fd, buf, str_len); // echo!
}
}
}
}
}
close(serv_sock);
close(epfd);
return 0;
}
728x90
'Programming > Network' 카테고리의 다른 글
열혈 TCP/IP 18-2. 쓰레드의 생성 및 실행 (0) | 2021.03.28 |
---|---|
열혈 TCP/IP 18-1. 쓰레드의 이론적 이해 (0) | 2021.03.28 |
열혈 TCP/IP 17-1. epoll의 이해와 활용 (0) | 2021.03.28 |
열혈 TCP/IP 16-2. 파일 디스크립터의 복사와 half-close (0) | 2021.03.28 |
열혈 TCP/IP 16-1. 입력 / 출력 스트림의 분리 (0) | 2021.03.28 |