Epoll은 기존 select 모델의 한계를 뛰어넘는 새로운 스타일의 서버입니다.
기존 select의 경우 일정 비트열을 두고 그곳에 0 이냐 1이냐로 클라이언트의 응답을 기다리는 방식이라 약 300~500 클라이언트 이상 동접시 속도저하 문제가 나타난다고 합니다.
그래서 이를 해결하기 위해 아예 하드웨어적으로 클라이언트의 접속 변화를 감지해서 recv냐 send냐 를 판별해 주는 함수가 epoll 이라 합니다.
문제는 이게.. 리눅스에서 만든거라 윈도우에선 사용이 불가능합니다.
그리고 리눅스도 커널 2.6 이상에서만 된다고 합니다.
하여간 소스 들어갑니다.
(인터넷과 man 메뉴얼)로 짜집기 한겁니다.. orz
chatServer.c |
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/epoll.h> // 이게 중요 #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <fcntl.h> #include <sys/ioctl.h> #include <errno.h> #define EPOLL_SIZE 60 #define EPOLL_EVENT_SIZE 100 #define OPENPORT 9000 // open port 번호
int main (int argc, char **argv) { struct epoll_event *events; // epoll 관련 struct epoll_event ev; struct sockaddr_in addr, clientaddr; int clilen; int sfd, efd, cfd; // server 소켓, epoll 소켓, client 소켓 int i,j; // for문을 위해 int max_got_events; int result; int readn; int sendflags = 0; char buf_in[256] = { '\0' }; // 버퍼 int count = 0;
if ((efd = epoll_create (EPOLL_EVENT_SIZE)) < 0) { perror ("epoll_create (1) error"); return -1; } // init pool events = (struct epoll_event *) malloc (sizeof (*events) * EPOLL_SIZE); if (NULL == events) { perror ("epoll_create (0) error"); return -1; }
clilen = sizeof (clientaddr); sfd = socket (AF_INET, SOCK_STREAM, 0); if (sfd == -1) { perror ("socket error :"); close (sfd); return -1; }
addr.sin_family = AF_INET; addr.sin_port = htons (OPENPORT); addr.sin_addr.s_addr = htonl (INADDR_ANY); if (bind (sfd, (struct sockaddr *) &addr, sizeof (addr)) == -1) { close (sfd); return -2; } listen (sfd, 5); if (sfd < 0) { perror ("init_acceptsock error"); return 1; }
printf("Running Server port %d\n",OPENPORT); ev.events = EPOLLIN; ev.data.fd = sfd; result = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &ev); if (result < 0) { perror ("epoll_ctl error"); return 1; } // 서버 실제 루트 { // epoll 이벤트 대기 (클라이언트의 반응(접속 or send) 감지) { // 이벤트 들어온 소켓이 연결 요청이면 { cfd = accept (sfd, (struct sockaddr *) &clientaddr, &clilen); if (cfd < 0) { perror ("Accept error"); return -1; } ev.events = EPOLLIN; ev.data.fd = cfd; epoll_ctl (efd, EPOLL_CTL_ADD, cfd, &ev); printf("Accepted socket : %d\n",cfd); count++; } // 그게 아니면, { cfd = events[i].data.fd; memset (buf_in, 0x00, 256); readn = read (cfd, buf_in, 255); // if it occured ploblems with reading, // delete from epoll event pool and close socket // readn 값이 0 이하이면 recv 처리 if (readn <= 0) { epoll_ctl(efd, EPOLL_CTL_DEL, cfd, &ev); close (cfd); printf ("Close fd %d\n", cfd); count--; } else { // 아니면 send 처리 for(j=5; j<count+5; j++) { // 단 이벤트로온 client엔 send 하지말고 send (j, buf_in, strlen (buf_in), sendflags); } } } } } return 1; } |
맨 아래쪽의
For j=5 라고 했는데, 리눅스에선 소켓 번호가 윈도우와 달리 5번부터인가 시작되서 그렇습니다.
윈도우는 OS가 자기 맘대로 할당하지만 리눅스에선 0~4번이 정해져 있고 그 이후 차례대로 count 됩니다..
물론 저 소스는 야메로 짠거라 문제 발생 요지가 많습니다. Orz
참고로 클라이언트는…
그냥 telnet으로 telnet 127.0.0.1 9000 으로 접속하면 됩니다.
'리눅스 서버에 대해서' 카테고리의 다른 글
리눅스에서 스레드 프로그램 컴파일시 옵션 (0) | 2008.06.19 |
---|---|
리눅스에서 cpp파일로 컴파일할 때 주의점. (0) | 2008.05.27 |
Select 채팅 서버 (0) | 2008.05.25 |
쓰레드 에코서버 (0) | 2008.05.25 |
밑의 epoll 예제가 좀 복잡해서 좀 간단하게 수정함 (0) | 2008.03.29 |