Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

2.6 epoll 지원

이전의 epoll(:12)문서가 Kernel(:12) 2.4 기반으로, 2.6이 대중화된 현 시점에서는 너무 오래된 감이 있어서 2.6기반으로 재작성해보기로 했다. 예전 문서는 그대로 남겨두기로 했다. epoll에 대한 개괄적인 내용은 예전문서를 확인해 보기 바랍니다.

2.4에서 epoll은 정식지원 사항이 아니었지만, 2.6에서는 정식지원이 되는 관계로 별도의 커널패치라든지 라이브러리(:12)설치가 필요 없어졌습니다.

아래는 간단한 예제입니다. ET (EPOLLET) 방식이기 때문에 소켓을 nonblocking으로 했습니다. ET로 할것인지 LT로 할것인지에 대해서 생각이 다를 수 있는데, 속편하게 LT로 하는게 어떨까합니다.
#include <pthread.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>

#include <stdlib.h>
#include <unistd.h>

#define MAX_CLIENT 101 
#define PORT 3355 
#define MAX_LINE 1024
#define DEBUG 

int listenfd;

// nonblock 소켓생성 
void nonblock(int sockfd)
{
    int opts;
    opts = fcntl(sockfd, F_GETFL);
    if(opts < 0)
    {
        perror("fcntl(F_GETFL)\n");
        exit(1);
    }
    opts = (opts | O_NONBLOCK);
    if(fcntl(sockfd, F_SETFL, opts) < 0)
    {
        perror("fcntl(F_SETFL)\n");
        exit(1);
    }
}


int main(int argc, char **argv)
{
    int epfd;
    struct epoll_event *events;
    struct epoll_event ev;

    struct sockaddr_in srv;
    int clifd;
    int i;
    int n;
    int res;
    char buffer[MAX_LINE];

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("sockfd\n");
        exit(1);
    }

    events = malloc(sizeof(struct epoll_event) * MAX_CLIENT);

    bzero(&srv, sizeof(srv));
    srv.sin_family = AF_INET;
    srv.sin_addr.s_addr = INADDR_ANY;
    srv.sin_port = htons(PORT);

    if( bind(listenfd, (struct sockaddr *) &srv, sizeof(srv)) < 0)
    {
        perror("bind\n");
        exit(1);
    }

    listen(listenfd, 5);

    epfd = epoll_create(MAX_CLIENT);
    if(!epfd)
    {
        perror("epoll_create\n");
        exit(1);
    }
    ev.events = EPOLLIN | EPOLLHUP | EPOLLET;
    ev.data.fd = listenfd;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) < 0)
    {
        perror("epoll_ctl, adding listenfd\n");
        exit(1);
    }
    for( ; ; )
    {
        res = epoll_wait(epfd, events, MAX_CLIENT, 0);
        for(i = 0; i < res; i++)
        {
            if(events[i].data.fd == listenfd)
            {
                clifd = accept(listenfd, NULL, NULL);
                if(clifd > 0)
                {
                    nonblock(clifd);
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = clifd;
                    if(epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev) < 0)
                    {
                        perror("epoll_ctl ADD\n");
                        exit(1);
                    }
                }
            }
            else {
                memset(buffer, 0x00, MAX_LINE);
                n = recv(events[i].data.fd, buffer, MAX_LINE-1, 0);
                if(n == 0)
                {
#ifdef DEBUG 
                    printf("%d closed connection\n", events[i].data.fd);
                    epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
#endif
                }
                else if(n < 0)
                {
#ifdef DEBUG 
                    printf("%d error occured, errno: %d\n",
                            events[i].data.fd, errno);
#endif
                }
                else {
#ifdef DEBUG 
                    printf("%d data received: %s",
                            events[i].data.fd, buffer);
                    bzero(&buffer, strlen(buffer));
#endif
                    send(events[i].data.fd, buffer, strlen(buffer), 0);
                }
            }
        }
    }

    return 0;
}

Java1.5에 NIO가 포함되어 있던데, 리눅스(:12)에서의 NIO 는 결국 epoll의 구현이였다.