-
epoll server chatting app소프트웨어 및 프로그래밍/네트워크 프로그래밍 2020. 3. 26. 11:07
select
fd_set : 고정 비트 테이블 사용, 크기가 고정
이벤트가 발생 감재를 위해서는 순차검색, O(n)
데이터가 오면 기존 fd_set을 모두 변경
epoll
fd의 수가 무제한
fd를 커널에서 관리, 상태가 바뀐 것만을 직접 통지 --> O(1)
fd_set 복사 불필요
epoll 함수
epoll_create
epoll_ctl
epoll_wait
epoll_create
int fd_epoll; bool is_epoll_init = false; int EpollInit(int size) { if((fd_epoll = epoll_create(size)) > 0) is_epoll_init = true; return fd_epoll; }
int epoll_create(int size);
size 값은 정수인데 무작정 큰 수를 쓸 수는 없다.
• 예상되는 최대 동시접속 수로 한다.
• 운영체제가 이 숫자를 허용하는지 먼저 확인해야 한다.
• 서버의 한계(ServerLimits)를 숙지하고, 적당한 값을 써 주거나 서버한계를 늘려야 한다.epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int EpollAdd(const int fd) { struct epoll_event ev; ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; ev.data.fd = fd; return epoll_ctl(fd_epoll, EPOLL_CTL_ADD, fd, &ev); }
int op에 들어가는 flag
EPOLL_CTL_ADD : fd를 epfd의 관심 목록에 추가, 이미 목록에 존재한다면 EEXIST 에러를 발생, event 집한은 *ev에 저장
EPOLL_CTL_MOD : *ev에 지정된 정보를 이용해 fd 설정 변경, 관심 목록에 없는 fd라면 ENOENT 에러를 발생
EPOLL_CTL_DEL : epfd에서 fd를 제거, epfd 관심 목록에 없는 fd를 제거하려면 ENOENT 에러를 발생, fd를 닫으면 epoll 관심 목록에서 자동 제거
epoll_wait
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);관심 있는 fd들에 무슨 일이 일어났는지 조사, 사건들의 리스트를 (epoll_event).events[]의 배열로 전달.
실제 동시 접속수와는 상관없이 maxevents 파라미터로 최대 몇 개까지의 event만 처리할 것임을 지정해 주도록 하고 있음
maxevnets 파라미터를 통해 하번에 처리할 숫자를 제한
timeout 밀리세컨드 단위로 지정, 해당 시간 동안 사건 발생 기다림, 기다리는 도중 사건이 발생하면 즉시 리턴. 여기서 -1 을 할당하면 영원히 기다림(blocking), 0을 대입하면 polling과 비슷
#define MAX_EVENTS 100 // 최대 100개를 한번에 처리할 것이다. struct epoll_event events[MAX_EVENTS]; int nfds, n; for(;;){ // 발생한 사건의 갯수를 얻어낸다. 0인 경우는 아무 일도 발생하지 않은 것 nfds = epoll_wait(fd_epoll, events, MAX_EVENTS, 10); if(nfds < 0) { // critical error fprintf(stderr, "epoll_wait() error : %s\n", strerror(errno)); exit(-1); } // 아무 일도 일어나지 않았다. if(nfds == 0){ // idle continue; } for(n=0; n < nfds; ++n) OnEvent(&events[n]); } int OnEvent(const struct epoll_event *event) { int nread; char buf[1024]; if( event->events & EPOLLIN ){ nread = read(event->data.fd, buf, 1024); if( nread < 1){ fprintf(stdout, "nread returns : %d\n", nread); } else { fprintf(stdout, "data : %s\n", buf); buf[0] = 0; } } if( event->events & EPOLLOUT){ } if( event->events & EPOLLERR){ } return 1; }
EPOLLIN : 수신할 데이터가 존재하는 상황, EPOLL_CTL의 입력, WAIT의 출력에 모두 사용됨
EPOLLOUT : 출력버퍼가 비워져서 당장 데이터를 전송할 수 있는 상황, EPOLL_CTL의 입력, WAIT의 출력에 모두 사용됨
EPOLLPRI : OOB 데이터가 수신된 상황, EPOLL_CTL의 입력, WAIT의 출력에 모두 사용됨
EPOLLRDHUP : 연결이 종료되거나 Half-close가 진행된 상황, 이는 엣지 트리거 방식에서 유용하게 사용됨, 상대편 소켓 셧다운, WAIT의 출력에 모두 사용됨
EPOLLERR : 에러가 발생한 상황, EPOLL_WAIT의 출력으로만 사용됨
EPOLLHUP : 장애발생, EPOLL_WAIT의 출력으로만 사용됨
EPOLLET : 이벤트의 감지를 엣지 트리거 방식으로 동작, EPOLL_CTL 입력에만 사용됨
EPOLLONESHOT : 이벤트가 한번 감지가 되면, 해당 파일 디스크립터에서는 더 이상 이벤트를 발생시키지 않는다. 따라서 epoll_ctl 함수의 두번째 인자로 EPOLL_CTL_MOD를 전달해서 이벤트로 재설정, EPOLL_CTL 입력에만 사용됨
https://gist.github.com/jacking75/6e8cef4141e82811c806
epoll을 사용한 echo 서버
epoll을 사용한 echo 서버. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
https://github.com/RajivKurian/epoll-example
Edge Trigger 과 Level Trigger
epoll에서는 Edge Trigger와 level Trigger 둘 중 하나를 선택
기본은 Level Trigger
Level Trigger는 특정 준위가 유지되는 동안 감지
Edge Trigger는 특정 준위가 변화하는 시점에서만 감지
'소프트웨어 및 프로그래밍 > 네트워크 프로그래밍' 카테고리의 다른 글
gRPC 클라이언트 서비스를 생성할 때 채널 상태를 우선 파악해야 할까? (0) 2024.12.06