- #include <sys/socket.h>
- #include <sys/epoll.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <arpa/inet.h>
- #include <string.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <signal.h>
- #include <syslog.h>
- void become_daemon (void)
- {
- switch (fork()) { // fork off the parent process
- case -1: { printf ("error: fork()\n"); exit(0); }
- case 0: break;
- default: exit (0);
- }
- umask (0); // change the file mode mask
- setsid(); // creates a new session
- chdir ("/"); // change the current working directory
- close (STDIN_FILENO); close (STDOUT_FILENO); close (STDERR_FILENO); // close out the standard file descriptors
- }
- void tcp_keepalive(int tcp_fd)
- {
- // keepalive probe packets: no data and ACK set tcp packets
- int keepAlive = 1;
- if (setsockopt (tcp_fd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1) { syslog(LOG_INFO, "error: setsockopt() SO_KEEPALIVE\n"); exit (0); }
- int keepIdle = 1; // The time (in seconds) the connection needs to remain idle before TCP starts sending keepalive probes
- if (setsockopt (tcp_fd,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1) { syslog(LOG_INFO, "error: setsockopt() TCP_KEEPIDLE\n"); exit(0); }
- int keepInterval = 1; // The time (in seconds) between individual keepalive probes
- if (setsockopt (tcp_fd,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1) { syslog(LOG_INFO, "error: setsockopt() TCP_KEEPINTVL\n"); exit(0); }
- int keepCount = 3; // The maximum number of keepalive probes TCP should send before dropping the connection
- if (setsockopt (tcp_fd,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1) { syslog(LOG_INFO, "error: setsockopt() TCP_KEEPCNT\n"); exit(0); }
- }
- void close_clean(int fd)
- {
- // clean
- close (fd);
- return;
- }
- void do_use_fd (int fd, unsigned char fin_flag)
- {
- #define BUF_SIZE 1500
- unsigned char buf[BUF_SIZE+1]; bzero (buf, BUF_SIZE+1);
- int len;
- len = read (fd, buf, BUF_SIZE);
- if (len == -1) { // rst
- shutdown(fd, SHUT_RDWR); close_clean (fd);
- return;
- }
- if (len == 0) { // fin
- shutdown(fd, SHUT_RDWR); close_clean (fd);
- return;
- }
- while (len > 0) {
- // rx data hander begin
- if (sendto(fd, buf, len, MSG_DONTWAIT, NULL, 0) == -1) {
- if((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
- shutdown(fd, SHUT_RDWR); close_clean (fd); return;
- }
- syslog(LOG_INFO,"error: sendto() %d\n", errno); exit (0);
- }
- // rx data hander end
- len = -1;
- len = read (fd, buf, BUF_SIZE);
- if (len == -1) {
- if((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // Non-blocking I/O has been selected using O_NONBLOCK and no data was immediately available for reading
- break;
- } else { syslog(LOG_INFO,"error: read() errno != EAGAIN\n"); exit (0); }
- } else if (len == 0) {
- fin_flag = 1;
- }
- }
- if (fin_flag > 0) { // fin
- shutdown(fd, SHUT_RDWR); close_clean (fd);
- return;
- }
- return;
- }
- int main ()
- {
- become_daemon ();
- int listen_fd;
- if ((listen_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_INFO,"error: socket()\n"); exit (0); }
- struct sockaddr_in acceptAddress;
- acceptAddress.sin_family = PF_INET;
- acceptAddress.sin_addr.s_addr = htonl (INADDR_ANY);
- #define LISTEN_PORT 2000
- acceptAddress.sin_port = htons (LISTEN_PORT);
- if (bind (listen_fd, (struct sockaddr *)&acceptAddress, sizeof(struct sockaddr)) != 0) { syslog(LOG_INFO,"error: bind()\n"); exit (0); }
- listen (listen_fd, 10000);
- tcp_keepalive(listen_fd);
- int flag = 1;
- if (setsockopt(listen_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)) == -1) { syslog(LOG_INFO,"error: setsockopt(TCP_NODELAY)\n"); exit(0); } // Disable the Nagle (TCP No Delay) algorithm
- signal(SIGPIPE, SIG_IGN);
- int epoll_fd;
- epoll_fd = epoll_create(1);
- struct epoll_event ev, *events;
- #define MAX_EVENTS 100
- events = (struct epoll_event *) malloc(MAX_EVENTS*sizeof(struct epoll_event));
- bzero (events, MAX_EVENTS*sizeof(struct epoll_event));
- /*
- syn --> EPOLLIN
- data --> EPOLLIN
- fin --> EPOLLIN|EPOLLRDHUP read() return len(rx_buf)
- rst --> EPOLLIN read() return -1
- */
- ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
- ev.data.fd = listen_fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) != 0) { syslog(LOG_INFO,"error: epoll_ctl() listen_fd\n"); exit(0); }
- for(;;) {
- int n_fds;
- n_fds = epoll_wait (epoll_fd, events, MAX_EVENTS, -1);
- int i;
- for(i=0; i<n_fds; i++) {
- if (events[i].events & (EPOLLERR | EPOLLHUP)) { // error
- if (events[i].data.fd == listen_fd) { syslog(LOG_INFO,"error: listen_fd\n"); exit(0); }
- close_clean (events[i].data.fd); // close(fd) cause fd to be removed from all epoll sets automatically
- } else if (events[i].events & EPOLLRDHUP) { // fin
- do_use_fd(events[i].data.fd, 1);
- } else if (events[i].events & EPOLLIN) {
- if (events[i].data.fd == listen_fd) { // syn
- struct sockaddr_in clientAddr;
- bzero(&clientAddr, sizeof(struct sockaddr_in));
- int client_fd, len;
- len = sizeof (struct sockaddr_in);
- client_fd = accept (listen_fd, (struct sockaddr *)&clientAddr, (socklen_t *)&len); // accept() creates a new connected socket file descriptor
- if(client_fd < 0) { continue; }
- if (fcntl(client_fd, F_SETFL, O_NONBLOCK) != 0) { syslog(LOG_INFO,"error: fcntl(). exit\n"); exit (0); } // set client_fd nonblocking
- ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
- ev.data.fd = client_fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) { syslog(LOG_INFO,"error: epoll_ctl\n"); exit(0); }
- } else { do_use_fd(events[i].data.fd, 0); } // data | rst
- } else { syslog(LOG_INFO,"unknown event\n"); exit(0); }
- }
- }
- close (listen_fd);
- close (epoll_fd);
- free (events);
- return 0;
- }