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 }