Guest User

srv_epoll.c

a guest
Jul 14th, 2017
360
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 6.49 KB | None | 0 0
  1. #include <sys/epoll.h>
  2. #include <sys/uio.h>
  3. #include <stdio.h>
  4. #include <stdbool.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <signal.h>
  10. #include <sys/socket.h>
  11. #include <arpa/inet.h>
  12.  
  13. static void * xmalloc(size_t nelems, size_t elemsz)
  14. {
  15.     size_t n = nelems * elemsz;
  16.     void *r = malloc(n);
  17.     if (!r && n) {
  18.         fprintf(stderr, "Out of memory.\n");
  19.         abort();
  20.     }
  21.     return r;
  22. }
  23.  
  24. static int make_nonblock(int fd)
  25. {
  26.     int flags = fcntl(fd, F_GETFL);
  27.     if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
  28.         return -1;
  29.     }
  30.     return 0;
  31. }
  32.  
  33. //-----------------------------------------------------------------------------
  34.  
  35. enum { BUFSZ = 4 * 1024 };
  36. typedef struct {
  37.     union {
  38.         char buf[BUFSZ];
  39.         void *ptr;
  40.     } u;
  41.     size_t buf_start, buf_end;
  42.     int fd;
  43. } Datum;
  44.  
  45. #define datum_ptr(Dtm_) (Dtm_)->u.ptr
  46.  
  47. //-----------------------------------------------------------------------------
  48. // slot allocator
  49.  
  50. static Datum *sal_trash_top;
  51. static size_t sal_next_alloc = 1024; // must be >= 2
  52.  
  53. static Datum * sal_alloc(void)
  54. {
  55.     if (sal_trash_top) {
  56.         Datum *r = sal_trash_top;
  57.         sal_trash_top = datum_ptr(sal_trash_top);
  58.         return r;
  59.     } else {
  60.         Datum *r = xmalloc(sal_next_alloc, sizeof(Datum));
  61.         for (size_t i = 1; i < sal_next_alloc - 1; ++i) {
  62.             datum_ptr(r + i) = &r[i + 1];
  63.         }
  64.         datum_ptr(r + sal_next_alloc - 1) = NULL;
  65.         sal_trash_top = r + 1;
  66.         sal_next_alloc *= 2;
  67.         return r;
  68.     }
  69. }
  70.  
  71. static void sal_free(Datum *p)
  72. {
  73.     datum_ptr(p) = sal_trash_top;
  74.     sal_trash_top = p;
  75. }
  76.  
  77. //-----------------------------------------------------------------------------
  78.  
  79. static Datum * datum_create(int fd)
  80. {
  81.     Datum *d = sal_alloc();
  82.     d->buf_start = d->buf_end = 0;
  83.     d->fd = fd;
  84.     return d;
  85. }
  86.  
  87. static void datum_destroy(Datum *d)
  88. {
  89.     close(d->fd);
  90.     sal_free(d);
  91. }
  92.  
  93. #define datum_fd(Dtm_)  (Dtm_)->fd
  94.  
  95. static inline bool echo(Datum *d)
  96. {
  97.     int fd = d->fd;
  98.     char *buf = d->u.buf;
  99.  
  100.     size_t buf_start = d->buf_start;
  101.     size_t buf_end   = d->buf_end;
  102.     while (buf_start != buf_end) {
  103.         ssize_t w = write(fd, buf + buf_start, buf_end - buf_start);
  104.         if (w < 0) {
  105.             return errno == EAGAIN;
  106.         }
  107.         d->buf_start = (buf_start += w);
  108.     }
  109.  
  110.     while (1) {
  111.         ssize_t r = read(fd, buf, BUFSZ);
  112.         if (r == -1) {
  113.             return errno == EAGAIN;
  114.         } else if (r == 0) {
  115.             return false;
  116.         } else {
  117.             for (size_t written = 0; written != (size_t) r;) {
  118.                 ssize_t w = write(fd, buf + written, r - written);
  119.                 if (w < 0) {
  120.                     d->buf_start = written;
  121.                     d->buf_end = r;
  122.                     return errno == EAGAIN;
  123.                 }
  124.                 written += w;
  125.             }
  126.         }
  127.     }
  128.  
  129.     return true;
  130. }
  131.  
  132. //-----------------------------------------------------------------------------
  133.  
  134. enum { EPEVBUF_SIZE = 1024 };
  135.  
  136. void srv_epoll_run(int sfd)
  137. {
  138.     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
  139.         perror("signal");
  140.         return;
  141.     }
  142.     struct epoll_event *epevbuf = xmalloc(EPEVBUF_SIZE, sizeof(struct epoll_event));
  143.     int efd = epoll_create1(0);
  144.     if (efd < 0) {
  145.         perror("epoll_create1");
  146.         return;
  147.     }
  148.     if (make_nonblock(sfd) < 0) {
  149.         perror("make_nonblock (on server socket fd)");
  150.         return;
  151.     }
  152.     if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &(struct epoll_event) {
  153.             .events = EPOLLIN | EPOLLET,
  154.             .data.ptr = NULL,
  155.         }) < 0)
  156.     {
  157.         perror("epoll_ctl (EPOLL_CTL_ADD on sfd)");
  158.         return;
  159.     }
  160.     while (1) {
  161.         int nfds = epoll_wait(efd, epevbuf, EPEVBUF_SIZE, -1);
  162.         if (nfds < 0) {
  163.             if (errno == EINTR) {
  164.                 continue;
  165.             }
  166.             perror("epoll_wait");
  167.             return;
  168.         }
  169.         for (int i = 0; i < nfds; ++i) {
  170.             Datum *datum = epevbuf[i].data.ptr;
  171.             if (datum) {
  172.                 if (!echo(datum)) {
  173.                     if (epoll_ctl(efd, EPOLL_CTL_DEL, datum_fd(datum), NULL) < 0) {
  174.                         perror("epoll_ctl (EPOLL_CTL_DEL on client fd)");
  175.                         return;
  176.                     }
  177.                     datum_destroy(datum);
  178.                 }
  179.             } else {
  180.                 while (1) {
  181.                     int cfd = accept(sfd, NULL, NULL);
  182.                     if (cfd < 0) {
  183.                         break;
  184.                     }
  185.                     if (make_nonblock(cfd) < 0) {
  186.                         perror("make_nonblock (on client fd)");
  187.                         return;
  188.                     }
  189.                     if (epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &(struct epoll_event) {
  190.                             .events = EPOLLIN | EPOLLOUT | EPOLLET,
  191.                             .data.ptr = datum_create(cfd),
  192.                         }) < 0)
  193.                     {
  194.                         perror("epoll_ctl (EPOLL_CTL_ADD on cfd)");
  195.                         return;
  196.                     }
  197.                 }
  198.                 if (errno != EAGAIN) {
  199.                     perror("accept");
  200.                     return;
  201.                 }
  202.             }
  203.         }
  204.     }
  205. }
  206.  
  207. //-----------------------------------------------------------------------------
  208.  
  209. static void usage(void)
  210. {
  211.     fprintf(stderr, "USAGE: srv_epoll PORT\n");
  212.     exit(2);
  213. }
  214.  
  215. int main(int argc, char **argv)
  216. {
  217.     if (argc != 2) {
  218.         usage();
  219.     }
  220.     errno = 0;
  221.     char *endptr;
  222.     unsigned long port = strtoul(argv[1], &endptr, 10);
  223.     if (errno || endptr == argv[1] || *endptr != '\0') {
  224.         fprintf(stderr, "PORT is not an integer.\n");
  225.         usage();
  226.     }
  227.     if (port == 0 || port > 65535) {
  228.         fprintf(stderr, "PORT is not a valid port number.\n");
  229.         usage();
  230.     }
  231.  
  232.     int sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  233.     if (sfd < 0) {
  234.         perror("socket");
  235.         return 1;
  236.     }
  237.     struct sockaddr_in sa = {
  238.         .sin_family = PF_INET,
  239.         .sin_port = htons(port),
  240.         .sin_addr.s_addr = htonl(INADDR_ANY),
  241.     };
  242.     if (bind(sfd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
  243.         perror("bind");
  244.         return 1;
  245.     }
  246.     if (listen(sfd, SOMAXCONN) < 0) {
  247.         perror("listen");
  248.         return 1;
  249.     }
  250.     srv_epoll_run(sfd);
  251.     return 1;
  252. }
Advertisement
Add Comment
Please, Sign In to add comment