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