fnet.c

Simple C networking library
git clone git://git.finwo.net/lib/fnet.c
Log | Files | Refs | README

fnet.c (20319B)


      1 #include <errno.h>
      2 #include <stdbool.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #include <fcntl.h>
      8 #include <sys/types.h>
      9 
     10 #if defined(_WIN32) || defined(_WIN64)
     11 #include <sys/timeb.h>
     12 #include <winsock2.h>
     13 #include <Ws2tcpip.h>
     14 #else
     15 #include <netdb.h>
     16 #include <netinet/tcp.h>
     17 #include <sys/socket.h>
     18 #include <sys/time.h>
     19 #include <unistd.h>
     20 #endif
     21 
     22 #include "finwo/poll.h"
     23 #include "tidwall/buf.h"
     24 
     25 #include "fnet.h"
     26 
     27 #if defined(_WIN32) || defined(_WIN64)
     28 #define FNET_SOCKET unsigned int
     29 #pragma comment(lib,"Ws2_32.lib")
     30 bool w32_initialized = false;
     31 #else
     32 #define FNET_SOCKET int
     33 #endif
     34 
     35 struct fnet_internal_t {
     36   struct fnet_t ext; // KEEP AT TOP, allows casting between fnet_internal_t* and fnet_t*
     37   void          *prev;
     38   void          *next;
     39   FNET_SOCKET   *fds;
     40   int           nfds;
     41   FNET_FLAG     flags;
     42 };
     43 
     44 struct fnet_internal_t *connections = NULL;
     45 struct fpoll           *fpfd        = NULL;
     46 int                    runners      = 0;
     47 
     48 FNET_RETURNCODE setkeepalive(FNET_SOCKET fd) {
     49     if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int))) {
     50         return -1;
     51     }
     52 #if defined(__linux__)
     53     // tcp_keepalive_time
     54     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &(int){600}, sizeof(int))) {
     55         return FNET_RETURNCODE_ERROR;
     56     }
     57     // tcp_keepalive_intvl
     58     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &(int){60}, sizeof(int))) {
     59         return FNET_RETURNCODE_ERROR;
     60     }
     61     // tcp_keepalive_probes
     62     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &(int){6}, sizeof(int))) {
     63         return FNET_RETURNCODE_ERROR;
     64     }
     65 #endif
     66     return FNET_RETURNCODE_OK;
     67 }
     68 
     69 FNET_RETURNCODE settcpnodelay(FNET_SOCKET fd) {
     70   if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int))) {
     71     return FNET_RETURNCODE_ERROR;
     72   }
     73   return FNET_RETURNCODE_OK;
     74 }
     75 
     76 FNET_RETURNCODE setnonblock(FNET_SOCKET fd) {
     77   if (fd < 0) return FNET_RETURNCODE_ERROR;
     78 #if defined(_WIN32) || defined(_WIN64)
     79   unsigned long mode = 1;
     80   if (ioctlsocket(fd, FIONBIO, &mode) == 0) {
     81     return FNET_RETURNCODE_OK;
     82   } else {
     83     return FNET_RETURNCODE_ERROR;
     84   }
     85 #else
     86   int flags = fcntl(fd, F_GETFL, 0);
     87   if (flags < 0) return flags;
     88   if(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
     89     return FNET_RETURNCODE_ERROR;
     90   }
     91   return FNET_RETURNCODE_OK;
     92 #endif
     93 }
     94 
     95 int64_t _fnet_now() {
     96 #if defined(_WIN32) || defined(_WIN64)
     97   struct _timeb timebuffer;
     98   _ftime(&timebuffer);
     99   return (int64_t)(((timebuffer.time * 1000) + timebuffer.millitm));
    100 #else
    101   struct timeval tv;
    102   gettimeofday(&tv, NULL);
    103   return (tv.tv_sec * ((int64_t)1000)) + (tv.tv_usec / 1000);
    104 #endif
    105 }
    106 
    107 // CAUTION: assumes options have been vetted
    108 struct fnet_internal_t * _fnet_init(const struct fnet_options_t *options) {
    109   if (!fpfd) fpfd = fpoll_create();
    110 
    111   // 1-to-1 copy, don't touch the options
    112   struct fnet_internal_t *conn = malloc(sizeof(struct fnet_internal_t));
    113   conn->ext.proto     = options->proto;
    114   conn->ext.status    = FNET_STATUS_INITIALIZING;
    115   conn->ext.udata     = options->udata;
    116   conn->flags         = options->flags;
    117   conn->ext.onListen  = options->onListen;
    118   conn->ext.onConnect = options->onConnect;
    119   conn->ext.onData    = options->onData;
    120   conn->ext.onTick    = options->onTick;
    121   conn->ext.onClose   = options->onClose;
    122   conn->nfds          = 0;
    123   conn->fds           = NULL;
    124 
    125   // Aanndd add to the connection tracking list
    126   conn->next = connections;
    127   if (connections) connections->prev = conn;
    128   connections = conn;
    129 
    130   // Done
    131   return conn;
    132 }
    133 
    134 
    135 struct fnet_t * fnet_listen(const char *address, uint16_t port, const struct fnet_options_t *options) {
    136   struct fnet_internal_t *conn;
    137 
    138 #if defined(_WIN32) || defined(_WIN64)
    139   if (!w32_initialized) {
    140     WSADATA wsaData;
    141     int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
    142     if (err) {
    143       fprintf(stderr, "fnet_listen: WSAStartup\n");
    144       return NULL;
    145     }
    146     w32_initialized = true;
    147   }
    148 #endif
    149 
    150   // Checking arguments are given
    151   if (!address) {
    152     fprintf(stderr, "fnet_listen: address argument is required\n");
    153     return NULL;
    154   }
    155   if (!port) {
    156     fprintf(stderr, "fnet_listen: port argument is required\n");
    157     return NULL;
    158   }
    159   if (!options) {
    160     fprintf(stderr, "fnet_listen: options argument is required\n");
    161     return NULL;
    162   }
    163 
    164   // Check if we support the protocol
    165   switch(options->proto) {
    166     case FNET_PROTO_TCP:
    167       // Intentionally empty
    168       // TODO: tcp-specific arg validation
    169       break;
    170     default:
    171       fprintf(stderr, "fnet_listen: unknown protocol\n");
    172       return NULL;
    173   }
    174 
    175   // 1-to-1 copy, don't touch the options
    176   conn = _fnet_init(options);
    177 
    178   /* struct sockaddr_in servaddr; */
    179   struct addrinfo hints = {}, *addrs;
    180   char port_str[6] = {};
    181 
    182   hints.ai_family   = AF_UNSPEC;
    183   hints.ai_socktype = SOCK_STREAM;
    184   hints.ai_protocol = IPPROTO_TCP;
    185 
    186   // Get address info
    187   snprintf(port_str, sizeof(port_str), "%d", port);
    188   int ai_err = getaddrinfo(address, port_str, &hints, &addrs);
    189   if (ai_err != 0) {
    190     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ai_err));
    191     fnet_free((struct fnet_t *)conn);
    192     return NULL;
    193   }
    194 
    195   // Count the addresses to listen on
    196   // For example, "localhost" turned to "127.0.0.1" and "::1"
    197   int naddrs = 0;
    198   struct addrinfo *addrinfo = addrs;
    199   while(addrinfo) {
    200     naddrs++;
    201     addrinfo = addrinfo->ai_next;
    202   }
    203 
    204   conn->fds = malloc(naddrs * sizeof(FNET_SOCKET));
    205   if (!conn->fds) {
    206     fprintf(stderr, "%s\n", strerror(ENOMEM));
    207     fnet_free((struct fnet_t *)conn);
    208     freeaddrinfo(addrs);
    209     return NULL;
    210   }
    211 
    212   addrinfo = addrs;
    213   for (; addrinfo ; addrinfo = addrinfo->ai_next ) {
    214 
    215     FNET_SOCKET fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
    216     if (fd < 0) {
    217       fprintf(stderr, "socket\n");
    218       fnet_free((struct fnet_t *)conn);
    219       freeaddrinfo(addrs);
    220       return NULL;
    221     }
    222 
    223     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) {
    224       fprintf(stderr, "setsockopt(SO_REUSEADDR)\n");
    225       fnet_free((struct fnet_t *)conn);
    226       freeaddrinfo(addrs);
    227       return NULL;
    228     }
    229 
    230     if (setnonblock(fd) < 0) {
    231       fprintf(stderr, "setnonblock\n");
    232       fnet_free((struct fnet_t *)conn);
    233       freeaddrinfo(addrs);
    234       return NULL;
    235     }
    236 
    237     if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) < 0) {
    238       fprintf(stderr, "bind: %s\n", strerror(errno));
    239       fnet_free((struct fnet_t *)conn);
    240       freeaddrinfo(addrs);
    241       return NULL;
    242     }
    243 
    244     if (listen(fd, SOMAXCONN) < 0) {
    245       fprintf(stderr, "listen: %s\n", strerror(errno));
    246       fnet_free((struct fnet_t *)conn);
    247       freeaddrinfo(addrs);
    248       return NULL;
    249     }
    250 
    251     conn->fds[conn->nfds] = fd;
    252     conn->nfds++;
    253 
    254     if (fpfd) {
    255       fpoll_add(fpfd, FPOLL_IN | FPOLL_HUP, fd, conn);
    256     }
    257   }
    258 
    259   if (conn->ext.onListen) {
    260     conn->ext.onListen(&((struct fnet_ev){
    261       .connection = (struct fnet_t *)conn,
    262       .type       = FNET_EVENT_LISTEN,
    263       .buffer     = NULL,
    264       .udata      = conn->ext.udata,
    265     }));
    266   }
    267 
    268   freeaddrinfo(addrs);
    269   conn->ext.status = FNET_STATUS_LISTENING;
    270   return (struct fnet_t *)conn;
    271 }
    272 
    273 struct fnet_t * fnet_connect(const char *address, uint16_t port, const struct fnet_options_t *options) {
    274   struct fnet_internal_t *conn;
    275 
    276   // Checking arguments are given
    277   if (!address) {
    278     fprintf(stderr, "fnet_connect: address argument is required\n");
    279     return NULL;
    280   }
    281   if (!port) {
    282     fprintf(stderr, "fnet_connect: port argument is required\n");
    283     return NULL;
    284   }
    285   if (!options) {
    286     fprintf(stderr, "fnet_connect: options argument is required\n");
    287     return NULL;
    288   }
    289 
    290   // Check if we support the protocol
    291   switch(options->proto) {
    292     case FNET_PROTO_TCP:
    293       // Intentionally empty
    294       break;
    295     default:
    296       fprintf(stderr, "fnet_connect: unknown protocol\n");
    297       return NULL;
    298   }
    299 
    300   // 1-to-1 copy, don't touch the options
    301   conn = _fnet_init(options);
    302 
    303   /* struct sockaddr_in servaddr; */
    304   struct addrinfo hints = {}, *addrs;
    305   char port_str[6] = {};
    306 
    307   hints.ai_family   = AF_UNSPEC;
    308   hints.ai_socktype = SOCK_STREAM;
    309   hints.ai_protocol = IPPROTO_TCP;
    310 
    311   // Get address info
    312   snprintf(port_str, sizeof(port_str), "%d", port);
    313   int ai_err = getaddrinfo(address, port_str, &hints, &addrs);
    314   if (ai_err != 0) {
    315     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ai_err));
    316     fnet_free((struct fnet_t *)conn);
    317     return NULL;
    318   }
    319 
    320   // Count the addresses to listen on
    321   // For example, "localhost" turned to "127.0.0.1" and "::1"
    322   struct addrinfo *addrinfo = addrs;
    323   while(addrinfo) {
    324     addrinfo = addrinfo->ai_next;
    325   }
    326 
    327   conn->fds = malloc(sizeof(FNET_SOCKET));
    328   if (!conn->fds) {
    329     fprintf(stderr, "%s\n", strerror(ENOMEM));
    330     fnet_free((struct fnet_t *)conn);
    331     freeaddrinfo(addrs);
    332     return NULL;
    333   }
    334 
    335   addrinfo = addrs;
    336   for (; addrinfo ; addrinfo = addrinfo->ai_next ) {
    337 
    338     FNET_SOCKET fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
    339     if (fd < 0) {
    340       fprintf(stderr, "socket\n");
    341       fnet_free((struct fnet_t *)conn);
    342       freeaddrinfo(addrs);
    343       return NULL;
    344     }
    345 
    346     // Skip found address on failure to connect
    347     if (connect(fd, addrinfo->ai_addr, sizeof(struct sockaddr))) {
    348 #if defined(_WIN32) || defined(_WIN64)
    349       closesocket(fd);
    350 #else
    351       close(fd);
    352 #endif
    353       continue;
    354     }
    355 
    356     if (setnonblock(fd) < 0) {
    357 #if defined(_WIN32) || defined(_WIN64)
    358       closesocket(fd);
    359 #else
    360       close(fd);
    361 #endif
    362       fprintf(stderr, "setnonblock\n");
    363       fnet_free((struct fnet_t *)conn);
    364       freeaddrinfo(addrs);
    365       return NULL;
    366     }
    367 
    368     conn->fds[conn->nfds] = fd;
    369     conn->nfds++;
    370 
    371     if (fpfd) {
    372       fpoll_add(fpfd, FPOLL_IN | FPOLL_HUP, fd, conn);
    373     }
    374 
    375     // Only need 1 connection
    376     break;
    377   }
    378 
    379   freeaddrinfo(addrs);
    380 
    381   // Could not connect, might be unreachable, might be something else
    382   // We're not checking for the WHY here
    383   if (conn->nfds != 1) {
    384     fnet_free((struct fnet_t *)conn);
    385     return NULL;
    386   }
    387 
    388   conn->ext.status = FNET_STATUS_CONNECTED;
    389 
    390   if (conn->ext.onConnect) {
    391     conn->ext.onConnect(&((struct fnet_ev){
    392       .connection = (struct fnet_t *)conn,
    393       .type       = FNET_EVENT_CONNECT,
    394       .buffer     = NULL,
    395       .udata      = conn->ext.udata,
    396     }));
    397   }
    398 
    399   return (struct fnet_t *)conn;
    400 }
    401 
    402 FNET_RETURNCODE fnet_process(const struct fnet_t *connection) {
    403   struct fnet_internal_t *conn = (struct fnet_internal_t *)connection;
    404   struct fnet_internal_t *nconn = NULL;
    405   int i;
    406   FNET_SOCKET nfd;
    407   struct sockaddr_storage addr;
    408   socklen_t addrlen = sizeof(addr);
    409   struct buf *rbuf = NULL;
    410 
    411   char *tmp_buf = NULL;
    412   int   tmp_n   = 0;
    413 
    414   // Checking arguments are given
    415   if (!conn) {
    416     fprintf(stderr, "fnet_process: connection argument is required\n");
    417     return FNET_RETURNCODE_MISSING_ARGUMENT;
    418   }
    419 
    420   // No processing to be done here
    421   /* printf("Status:"); */
    422   /* printf((conn->ext.status & FNET_STATUS_INITIALIZING) ? " INITIALIZING" : ""); */
    423   /* printf((conn->ext.status & FNET_STATUS_LISTENING   ) ? " LISTENING"    : ""); */
    424   /* printf((conn->ext.status & FNET_STATUS_ERROR       ) ? " ERROR"        : ""); */
    425   /* printf((conn->ext.status & FNET_STATUS_CONNECTING  ) ? " CONNECTING"   : ""); */
    426   /* printf((conn->ext.status & FNET_STATUS_CONNECTED   ) ? " CONNECTED"    : ""); */
    427   /* printf((conn->ext.status & FNET_STATUS_CLOSED      ) ? " CLOSED"       : ""); */
    428   /* printf("\n"); */
    429   if (conn->ext.status & FNET_STATUS_INITIALIZING) return FNET_RETURNCODE_OK;
    430   if (conn->ext.status & FNET_STATUS_ERROR       ) return FNET_RETURNCODE_OK;
    431   if (conn->ext.status & FNET_STATUS_CLOSED      ) return FNET_RETURNCODE_OK;
    432 
    433   /* // Handle client still connecting */
    434   /* if (conn->ext.status & FNET_STATUS_CONNECTING) { */
    435   /*   // TODO: handle client connecting */
    436   /*   return FNET_RETURNCODE_NOT_IMPLEMENTED; */
    437   /* } */
    438 
    439   if (conn->ext.status & FNET_STATUS_CONNECTED) {
    440     rbuf       = malloc(sizeof(struct buf));
    441     tmp_buf    = malloc(BUFSIZ);
    442     rbuf->data = malloc(BUFSIZ);
    443     rbuf->cap  = BUFSIZ;
    444 
    445     for ( i = 0 ; i < conn->nfds ; i++ ) {
    446 
    447       rbuf->len = 0;
    448       do {
    449         tmp_n = recv(conn->fds[i], tmp_buf, BUFSIZ, 0);
    450         buf_append(rbuf, tmp_buf, tmp_n);
    451       } while(tmp_n == BUFSIZ);
    452 
    453       /* printf("Received %d bytes\n", rbuf->len); */
    454 
    455       if (rbuf->len < 0) {
    456         if (errno == EAGAIN) continue;
    457         buf_clear(rbuf);
    458         free(rbuf);
    459         free(tmp_buf);
    460         return FNET_RETURNCODE_ERRNO;
    461       }
    462 
    463       if (rbuf->len == 0) {
    464         fnet_close((struct fnet_t *)conn);
    465         break;
    466       }
    467       if (conn->ext.onData) {
    468         conn->ext.onData(&((struct fnet_ev){
    469           .connection = (struct fnet_t *)conn,
    470           .type       = FNET_EVENT_DATA,
    471           .buffer     = rbuf,
    472           .udata      = conn->ext.udata,
    473         }));
    474       }
    475     }
    476 
    477     buf_clear(rbuf);
    478     free(rbuf);
    479     free(tmp_buf);
    480     return FNET_RETURNCODE_OK;
    481   }
    482 
    483   if (conn->ext.status & FNET_STATUS_LISTENING) {
    484     /* printf("Processing %d listening fds\n", conn->nfds); */
    485     for ( i = 0 ; i < conn->nfds ; i++ ) {
    486       nfd = accept(conn->fds[i], (struct sockaddr *)&addr, &addrlen);
    487       /* printf("New sock: %d\n", nfd); */
    488 
    489       if (nfd < 0) {
    490         /* if (errno == EAGAIN     ) printf("Errno: EAGAIN(%d)\n", EAGAIN); */
    491         /* if (errno == EWOULDBLOCK) printf("Errno: EWOULDBLOCK(%d)\n", EWOULDBLOCK); */
    492         if (
    493           (errno == EAGAIN) ||
    494           (errno == EWOULDBLOCK)
    495         ) {
    496           // Simply no connections to accept
    497           /* printf("No new connections\n"); */
    498           continue;
    499         }
    500         // Indicate the errno is set
    501         return FNET_RETURNCODE_ERRNO;
    502       }
    503 
    504       // Make this one non-blocking and stay alive
    505       if (setnonblock(nfd) < 0) return FNET_RETURNCODE_ERROR;
    506       if (setkeepalive(nfd) < 0) return FNET_RETURNCODE_ERROR;
    507 
    508       // Create new fnet_t instance
    509       // _init already tracks the connection
    510       nconn = _fnet_init(&((struct fnet_options_t){
    511         .proto     = conn->ext.proto,
    512         .flags     = conn->flags & (~FNET_FLAG_RECONNECT),
    513         .onListen  = NULL,
    514         .onConnect = NULL,
    515         .onData    = NULL,
    516         .onTick    = NULL,
    517         .onClose   = NULL,
    518         .udata     = NULL,
    519       }));
    520 
    521       nconn->fds        = malloc(sizeof(FNET_SOCKET));
    522       nconn->fds[0]     = nfd;
    523       nconn->nfds       = 1;
    524       nconn->ext.status = FNET_STATUS_CONNECTED | FNET_STATUS_ACCEPTED;
    525 
    526       if (conn->ext.onConnect) {
    527         conn->ext.onConnect(&((struct fnet_ev){
    528           .connection = (struct fnet_t *)nconn,
    529           .type       = FNET_EVENT_CONNECT,
    530           .buffer     = NULL,
    531           .udata      = conn->ext.udata,
    532         }));
    533       }
    534 
    535       if (fpfd) {
    536         fpoll_add(fpfd, FPOLL_IN | FPOLL_HUP, nfd, nconn);
    537       }
    538     }
    539 
    540     // TODO: handle client connection
    541     return FNET_RETURNCODE_OK;
    542   }
    543 
    544   return FNET_RETURNCODE_OK;
    545 }
    546 
    547 FNET_RETURNCODE fnet_write(const struct fnet_t *connection, struct buf *buf) {
    548   struct fnet_internal_t *conn = (struct fnet_internal_t *)connection;
    549 
    550   // Checking arguments are given
    551   if (!conn) {
    552     fprintf(stderr, "fnet_write: connection argument is required\n");
    553     return FNET_RETURNCODE_MISSING_ARGUMENT;
    554   }
    555   if (!buf) {
    556     fprintf(stderr, "fnet_write: buf argument is required\n");
    557     return FNET_RETURNCODE_MISSING_ARGUMENT;
    558   }
    559 
    560   // A listening socket is not a connection
    561   if (conn->ext.status & FNET_STATUS_LISTENING) {
    562     fprintf(stderr, "fnet_write: Writing to a listening socket is not possible\n");
    563     return FNET_RETURNCODE_UNPROCESSABLE;
    564   }
    565 
    566   // How would I do this?? :S
    567   if (conn->nfds > 1) {
    568     fprintf(stderr, "fnet_write: Only connections with 1 file descriptor supported\n");
    569     return FNET_RETURNCODE_NOT_IMPLEMENTED;
    570   }
    571   if (conn->nfds < 1) {
    572     fprintf(stderr, "fnet_write: Only connections with 1 file descriptor supported\n");
    573     return FNET_RETURNCODE_NOT_IMPLEMENTED;
    574   }
    575 
    576   int n = 0;
    577   ssize_t r;
    578   while(n < buf->len) {
    579     r = send(conn->fds[0], &(buf->data[n]), buf->len - n, 0);
    580     // Handle errors
    581     if (r < 0) {
    582       if (errno == EAGAIN) {
    583         continue; // Try again
    584       }
    585       // We don't have a way to handle this error (yet)
    586       fprintf(stderr, "fnet_write: Unable to write to connection\n");
    587       return FNET_RETURNCODE_ERRNO;
    588     }
    589     // Increment counter with amount of bytes written
    590     // Allows for a partial write to not corrupt the data stream
    591     n += r;
    592   }
    593 
    594   return FNET_RETURNCODE_OK;
    595 }
    596 
    597 FNET_RETURNCODE fnet_close(const struct fnet_t *connection) {
    598   /* printf("Internal fnet_close\n"); */
    599   struct fnet_internal_t *conn = (struct fnet_internal_t *)connection;
    600   FNET_CALLBACK(cb) = NULL;
    601   int i;
    602 
    603   // Checking arguments are given
    604   if (!conn) {
    605     fprintf(stderr, "fnet_close: connection argument is required\n");
    606     return FNET_RETURNCODE_MISSING_ARGUMENT;
    607   }
    608 
    609   if (conn->nfds) {
    610     for ( i = 0 ; i < conn->nfds ; i++ ) {
    611       if (fpfd) {
    612         fpoll_del(fpfd, ~0, conn->fds[i]);
    613       }
    614 #if defined(_WIN32) || defined(_WIN64)
    615       closesocket(conn->fds[i]);
    616 #else
    617       close(conn->fds[i]);
    618 #endif
    619     }
    620     conn->nfds = 0;
    621     free(conn->fds);
    622     conn->fds = NULL;
    623   }
    624 
    625   conn->ext.status = FNET_STATUS_CLOSED;
    626 
    627   if (conn->ext.onClose) {
    628     cb = conn->ext.onClose;
    629     conn->ext.onClose = NULL;
    630 
    631     cb(&((struct fnet_ev){
    632       .connection = (struct fnet_t *)conn,
    633       .type       = FNET_EVENT_CLOSE,
    634       .buffer     = NULL,
    635       .udata      = conn->ext.udata,
    636     }));
    637   }
    638 
    639   return FNET_RETURNCODE_OK;
    640 }
    641 
    642 FNET_RETURNCODE fnet_free(struct fnet_t *connection) {
    643   struct fnet_internal_t *conn = (struct fnet_internal_t *)connection;
    644 
    645   // Checking arguments are given
    646   if (!conn) {
    647     fprintf(stderr, "fnet_free: connection argument is required\n");
    648     return FNET_RETURNCODE_MISSING_ARGUMENT;
    649   }
    650 
    651   // Remove ourselves from the linked list
    652   if (conn->next) ((struct fnet_internal_t *)(conn->next))->prev = conn->prev;
    653   if (conn->prev) ((struct fnet_internal_t *)(conn->prev))->next = conn->next;
    654   if (conn == connections) connections = conn->next;
    655 
    656   fnet_close((struct fnet_t *)conn);
    657 
    658   if (conn->fds) free(conn->fds);
    659 
    660   free(conn);
    661 
    662   return FNET_RETURNCODE_OK;
    663 }
    664 
    665 FNET_RETURNCODE fnet_tick(int doProcess) {
    666   struct fnet_internal_t *conn = connections;
    667   FNET_RETURNCODE ret;
    668   while(conn) {
    669     if (doProcess) {
    670       ret = fnet_process((struct fnet_t *)conn);
    671       if (ret < 0) return ret;
    672     }
    673     if (conn->ext.onTick) {
    674       conn->ext.onTick(&((struct fnet_ev){
    675         .connection = (struct fnet_t *)conn,
    676         .type       = FNET_EVENT_TICK,
    677         .buffer     = NULL,
    678         .udata      = conn->ext.udata,
    679       }));
    680     }
    681     conn = conn->next;
    682   }
    683   return FNET_RETURNCODE_OK;
    684 }
    685 
    686 void fnet_thread() {
    687   fnet_main();
    688 }
    689 
    690 FNET_RETURNCODE fnet_main() {
    691   FNET_RETURNCODE ret;
    692   int64_t         ttime = _fnet_now();
    693   int64_t         tdiff = 0;
    694   int             ev_count;
    695   int             i;
    696 
    697   if (runners) {
    698     return FNET_RETURNCODE_ALREADY_ACTIVE;
    699   }
    700 
    701   runners++;
    702 
    703   struct fpoll_ev events[8];
    704 
    705   while(runners) {
    706 
    707     // Do the actual processing
    708     if (fpfd) {
    709       ev_count = fpoll_wait(fpfd, events, 8, tdiff);
    710       /* if (ev_count) { */
    711       /*   printf("New events: %d\n", ev_count); */
    712       /* } */
    713       for( i = 0 ; i < ev_count ; i++ ) {
    714         /* printf("EV:"); */
    715         /* printf((events[i].ev & FPOLL_IN ) ? " IN" : ""); */
    716         /* printf((events[i].ev & FPOLL_OUT) ? " OUT" : ""); */
    717         /* printf((events[i].ev & FPOLL_HUP) ? " HUP" : ""); */
    718         /* printf("\n"); */
    719         ret = fnet_process((struct fnet_t *)events[i].udata);
    720         if (ret) return ret;
    721       }
    722     } else {
    723       fnet_tick(1);
    724     }
    725 
    726     // Tick timing
    727     tdiff = ttime - _fnet_now();
    728     if (fpfd && (tdiff <= 0)) {
    729       ttime += 1000;
    730       tdiff += 1000;
    731       fnet_tick(0);
    732     }
    733 
    734     // Sleep if no epoll
    735     if (!fpfd) {
    736       /* printf("No poll, do tick\n"); */
    737       ttime += 1000;
    738 #if defined(_WIN32) || defined(_WIN64)
    739       Sleep(tdiff);
    740 #else
    741       usleep(tdiff * 1000);
    742 #endif
    743     }
    744   }
    745 
    746   /* printf("fnet_main finished\n"); */
    747 
    748   return FNET_RETURNCODE_OK;
    749 }
    750 
    751 FNET_RETURNCODE fnet_shutdown() {
    752   runners = 0;
    753   while(connections) fnet_free((struct fnet_t *)connections);
    754 #if defined(_WIN32) || defined(_WIN64)
    755   WSACleanup();
    756 #endif
    757   return FNET_RETURNCODE_OK;
    758 }