Computer/Network

epoll 예제

수지밝은미소 2008. 1. 8. 18:57

<epoll>

 

#include <stdio.h>
#include <stdlib.h>#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>  // epoll system call 쓰기위한 헤더
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

#define MAXPENDING      5
#define BUFSIZE         256
#define MAXSOCKET       10000   // 감시할 최대 소켓 수

/* 임의로 정의한 소켓, 버퍼를 가지는 구조체 */
typedef struct
{
        int fd;
        char buffer[BUFSIZE];
} epoll_data;

int eventpoll;  // epoll 디스크립터
struct epoll_event serv_event;          // 서버쪽 소켓에대한 epoll 세팅 구조체
struct epoll_event ev[MAXSOCKET];       // 클라이언트 소켓들을 관리하기 위한 구조체

int serv_sock,clnt_sock;
int connection=0;

void DieWithError(char *string)
{
        perror(string);
        exit(1);
}

int main(int argc, char *argv[])
{
        int nready,n;
        ssize_t nread;
        socklen_t clnt_len;
        epoll_data *data;
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        unsigned short serv_port;
        unsigned int set = 1;

        if(argc!=2)
        {
                fprintf(stderr,"Usage: %s <Server Port>\n",argv[0]);
                exit(1);
        }
        serv_port=atoi(argv[1]);

        /* 소켓 생성 */
        serv_sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

        memset(&serv_addr,0,sizeof(serv_addr));
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        serv_addr.sin_port=htons(serv_port);

        setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set));

        if(bind(serv_sock,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
                DieWithError("bind() error");

        /* epoll 디스크립터 생성 */
        if((eventpoll=epoll_create(MAXSOCKET))<0)
                DieWithError("epoll_create error");

        if(listen(serv_sock,MAXPENDING)<0)
                DieWithError("listen() error");

        /* 소켓을 논블록킹 방식으로 변경 */
        if(fcntl(serv_sock,F_SETFL,O_NONBLOCK)<0)
                DieWithError("set non-blocking error");

        /* 서버쪽 소켓에 대한 epoll 셋팅 */
        serv_event.events=EPOLLIN|EPOLLET;      // 읽기, 경계인식방식으로 셋팅
        serv_event.data.fd=serv_sock;           // 서버 소켓 셋팅

        /* epoll에 소켓과 감시할 이벤트를 등록 */
        if(epoll_ctl(eventpoll,EPOLL_CTL_ADD,serv_sock,&serv_event)<0)
                DieWithError("epoll_ctl error");

        for(;;)
        {
                /*
                        감시하고 있는 소켓에서 변화 감지 ,
                        감지된 소켓 수를 리턴
                        변화감지된 소켓의 정보는 순서대로 ev구조체 배열에 저장
                */
                nready = epoll_wait(eventpoll, ev, MAXSOCKET, -1);
                if(nready<0)
                        DieWithError("epoll_wait error");

                /* 변화가 생긴 수만큼 루프 수행 */
                for(n=0;n<nready;n++)
                {
                        /* 변화가 생긴 소켓이 서버 소켓인지 조사 (서버 소켓에 읽기 변화가 감지된 경우) */
                        if(ev[n].data.fd == serv_sock)
                        {
                                struct epoll_event event;
                                clnt_len=sizeof(clnt_addr);

                                /* accept */
                                if((clnt_sock=accept(serv_sock,(struct sockaddr *)&clnt_addr,&clnt_len))<0)
                                        DieWithError("accept() failed");

                                /* 클라이언트의 소켓도 논블럭으로 지정 */
                                if(fcntl(clnt_sock,F_SETFL,O_NONBLOCK)<0)
                                        DieWithError("set non-blocking error");

                                data=(epoll_data*)malloc(sizeof(epoll_data));

                                /* 읽기 감지, 경계인식 방식 */
                                event.events= EPOLLIN | EPOLLET;

                                data->fd=clnt_sock;
                                memset(data->buffer,0,sizeof(data->buffer));

                                /* epoll_event 구조체의 포인터가 사용자가 만든 소켓-버퍼 구조체를 가리키게 한다 */
                                event.data.ptr=data;

                                /* 클라이언트 소켓을 epoll에 등록 */
                                if(epoll_ctl(eventpoll,EPOLL_CTL_ADD,clnt_sock,&event)<0)
                                        DieWithError("epoll_ctl error");

                                printf("client[%d] connected (Address: %s)\n",clnt_sock-4,inet_ntoa(clnt_addr.sin_addr));
                        }
                        /* 클라이언트 소켓에 변화가 생긴 경우 */
                        else
                        {
                                /* epoll 에 저장된 사용자 데이터를 복구 */
                                data=ev[n].data.ptr;

                                if((nread=recv(data->fd,data->buffer,sizeof(data->buffer),0))<0)
                                {
                                        /* 응답이 없는 경우 */
                                        if (errno==ECONNRESET)
                                        {
                                                printf("client[%d] aborted connection\n",data->fd-4);
                                                /* epoll에서 제거 */
                                                if(epoll_ctl(eventpoll,EPOLL_CTL_DEL,data->fd,ev)<0)
                                                        DieWithError("epoll_ctl error");
                                                close(data->fd);
                                        } else
                                                DieWithError("read error");
                                /* 접속 종료 */
                                } else if(nread==0) {

                                        printf("client[%d] closed connection\n",data->fd-4);
                                        /* epoll에서 제거 */
                                        if(epoll_ctl(eventpoll,EPOLL_CTL_DEL,data->fd,ev)<0)
                                                DieWithError("epoll_ctl error");
                                        close(data->fd);
                                } else {
                                        if(send(data->fd,data->buffer,strlen(data->buffer),0)!=strlen(data->buffer))
                                                DieWithError("send() error");
                                }
                        }
                }
        }
}