akaros/tests/epoll_server.c
<<
>>
Prefs
   1/* Copyright (c) 2014 The Regents of the University of California
   2 * Copyright (c) 2015 Google, Inc.
   3 * Barret Rhoden <brho@cs.berkeley.edu>
   4 * See LICENSE for details.
   5 *
   6 * Echo server using epoll, runs on port 23.  Main purpose is epoll testing.
   7 *
   8 * If you want to build the BSD sockets version, you need to comment out the
   9 * #define for PLAN9NET. */
  10
  11/* Comment this out for BSD sockets */
  12//#define PLAN9NET
  13
  14#include <stdlib.h>
  15#include <stdio.h>
  16#include <parlib/parlib.h>
  17#include <unistd.h>
  18#include <parlib/event.h>
  19#include <benchutil/measure.h>
  20#include <parlib/uthread.h>
  21#include <parlib/timing.h>
  22
  23#include <sys/types.h>
  24#include <sys/stat.h>
  25#include <fcntl.h>
  26
  27#include <sys/epoll.h>
  28
  29#ifdef PLAN9NET
  30
  31#include <iplib/iplib.h>
  32
  33#else
  34
  35#include <sys/types.h>
  36#include <sys/socket.h>
  37#include <netinet/in.h>
  38#include <arpa/inet.h>
  39
  40#endif
  41
  42int main()
  43{
  44        int ret;
  45        int afd, dfd, lcfd, listen_fd;
  46        char adir[40], ldir[40];
  47        int n;
  48        char buf[256];
  49        /* We'll use this to see if we actually did epoll_waits instead of
  50         * blocking calls.  It's not 100%, but with a human on the other end, it
  51         * should be fine. */
  52        bool has_epolled = FALSE;
  53
  54#ifdef PLAN9NET
  55        printf("Using Plan 9's networking stack\n");
  56        /* This clones a conversation (opens /net/tcp/clone), then reads the
  57         * cloned fd (which is the ctl) to givure out the conv number (the
  58         * line), then writes "announce [addr]" into ctl.  This "announce"
  59         * command often has a "bind" in it too.  plan9 bind just sets the local
  60         * addr/port.  TCP announce also does this.  Returns the ctlfd. */
  61        afd = announce9("tcp!*!23", adir, 0);
  62
  63        if (afd < 0) {
  64                perror("Announce failure");
  65                return -1;
  66        }
  67        printf("Announced on line %s\n", adir);
  68#else
  69        printf("Using the BSD socket shims over Plan 9's networking stack\n");
  70        int srv_socket, con_socket;
  71        struct sockaddr_in dest, srv = {0};
  72        srv.sin_family = AF_INET;
  73        srv.sin_addr.s_addr = htonl(INADDR_ANY);
  74        srv.sin_port = htons(23);
  75        socklen_t socksize = sizeof(struct sockaddr_in);
  76
  77        /* Equiv to cloning a converstation in plan 9.  The shim returns the
  78         * data FD for the conversation. */
  79        srv_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
  80        if (srv_socket < 0) {
  81                perror("Socket failure");
  82                return -1;
  83        }
  84        /* bind + listen is equiv to announce() in plan 9.  Note that the "bind"
  85         * command is used, unlike in the plan9 announce. */
  86        /* Binds our socket to the given addr/port in srv. */
  87        ret = bind(srv_socket, (struct sockaddr*)&srv,
  88                   sizeof(struct sockaddr_in));
  89        if (ret < 0) {
  90                perror("Bind failure");
  91                return -1;
  92        }
  93        /* marks the socket as a listener/server */
  94        ret = listen(srv_socket, 1);
  95        if (ret < 0) {
  96                perror("Listen failure");
  97                return -1;
  98        }
  99        printf("Listened on port %d\n", ntohs(srv.sin_port));
 100#endif
 101
 102        /* at this point, the server has done all the prep necessary to be able
 103         * to sleep/block/wait on an incoming connection. */
 104        #define EP_SET_SZ 10    /* this is actually the ID of the largest FD */
 105        int epfd = epoll_create(EP_SET_SZ);
 106        struct epoll_event ep_ev;
 107        struct epoll_event results[EP_SET_SZ];
 108
 109        if (epfd < 0) {
 110                perror("epoll_create");
 111                exit(-1);
 112        }
 113        ep_ev.events = EPOLLIN | EPOLLET;
 114
 115#ifdef PLAN9NET
 116
 117        snprintf(buf, sizeof(buf), "%s/listen", adir);
 118        listen_fd = open(buf, O_PATH);
 119        if (listen_fd < 0){
 120                perror("listen fd");
 121                return -1;
 122        }
 123        /* This is a little subtle.  We're putting a tap on the listen file /
 124         * listen_fd.  When this fires, we get an event because of that
 125         * listen_fd.  But we don't actually listen or do anything to that
 126         * listen_fd.  It's solely for monitoring.  We open a path, below, and
 127         * we'll reattempt to do *that* operation when someone tells us that our
 128         * listen tap fires. */
 129        ep_ev.data.fd = listen_fd;
 130        if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ep_ev)) {
 131                perror("epoll_ctl_add listen");
 132                exit(-1);
 133        }
 134        has_epolled = FALSE;
 135        while (1) {
 136                /* Opens the conversation's listen file.  This blocks til
 137                 * someone connects.  When they do, a new conversation is
 138                 * created, and that open returned an FD for the new conv's ctl.
 139                 * listen() reads that to find out the conv number (the line)
 140                 * for this new conv.  listen() returns the ctl for this new
 141                 * conv.
 142                 *
 143                 * Non-block is for the act of listening, and applies to lcfd.
 144                 */
 145                lcfd = listen9(adir, ldir, O_NONBLOCK);
 146                if (lcfd >= 0)
 147                        break;
 148                if (errno != EAGAIN) {
 149                        perror("Listen failure");
 150                        return -1;
 151                }
 152                if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
 153                        perror("epoll_wait");
 154                        exit(-1);
 155                }
 156                has_epolled = TRUE;
 157                assert(results[0].data.fd == listen_fd);
 158                assert(results[0].events == EPOLLIN);
 159        }
 160        printf("Listened and got line %s\n", ldir);
 161        assert(has_epolled);
 162
 163        /* No longer need listen_fd.  You should CTL_DEL before closing. */
 164        if (epoll_ctl(epfd, EPOLL_CTL_DEL, listen_fd, &ep_ev)) {
 165                perror("epoll_ctl_del");
 166                exit(-1);
 167        }
 168        close(listen_fd);
 169
 170        /* Writes "accept [NUM]" into the ctlfd, then opens the conv's data file
 171         * and returns that fd.  Writing "accept" is a noop for most of our
 172         * protocols.  */
 173        dfd = accept9(lcfd, ldir);
 174        if (dfd < 0) {
 175                perror("Accept failure");
 176                return -1;
 177        }
 178
 179#else
 180
 181        ep_ev.data.fd = srv_socket;
 182        if (epoll_ctl(epfd, EPOLL_CTL_ADD, srv_socket, &ep_ev)) {
 183                perror("epoll_ctl_add srv_socket");
 184                exit(-1);
 185        }
 186        has_epolled = FALSE;
 187        while (1) {
 188                /* returns an FD for a new socket. */
 189                dfd = accept(srv_socket, (struct sockaddr*)&dest, &socksize);
 190                if (dfd >= 0)
 191                        break;
 192                if (errno != EAGAIN) {
 193                        perror("Accept failure");
 194                        return -1;
 195                }
 196                if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
 197                        perror("epoll_wait");
 198                        exit(-1);
 199                }
 200                has_epolled = TRUE;
 201                assert(results[0].data.fd == srv_socket);
 202                assert(results[0].events == EPOLLIN);
 203        }
 204        printf("Accepted and got dfd %d\n", dfd);
 205        assert(has_epolled);
 206        if (epoll_ctl(epfd, EPOLL_CTL_DEL, srv_socket, &ep_ev)) {
 207                perror("epoll_ctl_del");
 208                while (1);
 209                exit(-1);
 210        }
 211
 212#endif
 213
 214        /* In lieu of accept4, we set the new socket's nonblock status manually.
 215         * Both OSs do this.  */
 216        ret = fcntl(dfd, F_SETFL, O_NONBLOCK);
 217        if (ret < 0) {
 218                perror("setfl dfd");
 219                exit(-1);
 220        }
 221        ep_ev.data.fd = dfd;
 222        if (epoll_ctl(epfd, EPOLL_CTL_ADD, dfd, &ep_ev)) {
 223                perror("epoll_ctl_add dvd");
 224                exit(-1);
 225        }
 226        /* echo until EOF */
 227        has_epolled = FALSE;
 228        printf("Server read: ");
 229        while (1) {
 230                while ((n = read(dfd, buf, sizeof(buf))) > 0) {
 231                        for (int i = 0; i < n; i++)
 232                                printf("%c", buf[i]);
 233                        fflush(stdout);
 234                        /* Should epoll on this direction too. */
 235                        if (write(dfd, buf, n) < 0) {
 236                                perror("writing");
 237                                exit(-1);
 238                        }
 239                }
 240                if (n == 0)
 241                        break;
 242                if (epoll_wait(epfd, results, EP_SET_SZ, -1) != 1) {
 243                        perror("epoll_wait 2");
 244                        exit(-1);
 245                }
 246                has_epolled = TRUE;
 247                assert(results[0].data.fd == dfd);
 248                /* you might get a HUP, but keep on reading! */
 249        }
 250        assert(has_epolled);
 251        if (epoll_ctl(epfd, EPOLL_CTL_DEL, dfd, &ep_ev)) {
 252                perror("epoll_ctl_del dfd");
 253                exit(-1);
 254        }
 255
 256#ifdef PLAN9NET
 257        close(dfd);             /* data fd for the new conv, from listen */
 258        close(lcfd);            /* ctl fd for the new conv, from listen */
 259        close(afd);             /* ctl fd for the listening conv */
 260#else
 261        close(dfd);             /* new connection socket, from accept */
 262        close(srv_socket);
 263#endif
 264}
 265