/* epoll_srv.c epoll test code for connection handling jve - nov 2008 compile with : gcc -O3 -falign-functions=4 -falign-jumps -falign-loops -Wall -o epoll_srv epoll_srv.c don't forget to fix the number of file descriptor with 'ulimit -n ' by default, on linux, it's 1024 but you can easily set up over 40000 */ #include #include #include #include #include #include #include #define DATABUFFERSIZE 1500 // file descriptor for listen socket int listenfd; // function to set the socket in non blocking mode void setnonblocking(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[]) { if(argc != 3) { printf("epoll test server\nusage : epoll_srv \n\n"); exit(EXIT_FAILURE); } int listenport = atoi(argv[1]); //port to listen on int maxconn = atoi(argv[2]); //max number off connection struct sockaddr_in srv; // server network struct int clientfd; //file descriptor to get data received from a client char buffer[DATABUFFERSIZE]; // for the data received int length; // data length received and sent int yes = 1; // why not... static struct epoll_event ev, *events; // epoll events structures int epfd; // file descriptor for epoll calls int res = 0; //events counter int i; // ooohhh let's loop again, come onnn let'ss loooopppp (chubby checker looping style) // what the server answers to every packets it receives //char ok[] = "HTTP/1.1 200 OK\nDate: Sun, 23 Nov 2008 11:00:22 GMT\nServer: epoll_srv\nLast-Modified: Sun, 12 Oct 2008 16:37:21 GMT\nETag: \"13c004-835-45910ff18b240\"\nAccept-Ranges: bytes\nContent-Length: 10\nConnection: close\n\n0000000000\n"; char ok[] = "ok"; // bind the listen socket on chosen port if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("sockfd\n"); exit(1); } if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) { perror("setsockopt"); exit(EXIT_FAILURE); } srv.sin_family = AF_INET; srv.sin_addr.s_addr = INADDR_ANY; srv.sin_port = htons(listenport); if( bind(listenfd, (struct sockaddr *) &srv, sizeof(srv)) < 0) { perror("bind\n"); exit(1); } if (listen(listenfd, maxconn) == -1) { perror("listen"); exit(1); } // create the special epoll file descriptor epfd = epoll_create(maxconn); if(!epfd) { perror("epoll_create\n"); exit(1); } /* epoll will wake up for the following events : * EPOLLIN : The associated file is available for read(2) operations. * EPOLLERR : Error condition happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events. * EPOLLHUP : Hang up happened on the associated file descriptor. epoll_wait(2) will always wait for this event; it is not necessary to set it in events. */ ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; // allocate enough memory to handle all the events events = calloc(maxconn, sizeof(struct epoll_event)); // on event, this FD will be return ev.data.fd = listenfd; // add the socket to the epoll file descriptor if(epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) < 0) { perror("epoll_ctl, adding listenfd\n"); exit(1); } printf("System loaded... waiting for incoming connections\n"); for (;;) { /*Wait for events on the epoll file descriptor epfd for a maximum time of timeout milliseconds. The memory area pointed to by events will contain the events that will be available for the caller. Up to maxevents are returned by epoll_wait(2). The maxevents parameter must be greater than zero. Specifying a timeout of -1 makes epoll_wait(2) wait indefinitely, while specifying a timeout equal to zero makes epoll_wait(2) to return immediately even if no events are available (return code equal to zero). */ res = epoll_wait(epfd, events, maxconn, -1); // go through awake file descriptors for(i = 0; i < res; i++) { if(events[i].data.fd == listenfd) { clientfd = accept(listenfd, NULL, NULL); if (clientfd < 0){ perror("accept"); continue; } setnonblocking(clientfd); ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; ev.data.fd = clientfd; if(epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev) < 0) { perror("epoll_ctl ADD\n"); exit(1); } } else { memset(buffer,0x0,DATABUFFERSIZE); length = recv(events[i].data.fd, buffer, DATABUFFERSIZE, 0); if(length == 0) { //printf("%d closed connection\n", events[i].data.fd); epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL); close(events[i].data.fd); } else if(length < 0) { printf("%d error occured, errno: %d\n",events[i].data.fd, errno); } else { // reply a hello send(events[i].data.fd, ok, strlen(ok), 0); // then close the file descriptor close(events[i].data.fd); // and remove it from the epfd file descriptors list //epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL); } } } } return 0; }