diff options
Diffstat (limited to 'src/common/socket.cpp')
-rw-r--r-- | src/common/socket.cpp | 264 |
1 files changed, 147 insertions, 117 deletions
diff --git a/src/common/socket.cpp b/src/common/socket.cpp index fab3804..2acbeb8 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -1,51 +1,74 @@ -// $Id: socket.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $ -// original : core.c 2003/02/26 18:03:12 Rev 1.7 - -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <errno.h> +#include "socket.hpp" -#include <sys/socket.h> -#include <netinet/in.h> +#include <arpa/inet.h> #include <netinet/tcp.h> -#include <sys/time.h> -#include <unistd.h> +#include <sys/socket.h> +//#include <sys/types.h> #include <fcntl.h> -#include <string.h> +#include <unistd.h> -#include "mmo.hpp" // [Valaris] thanks to fov -#include "socket.hpp" +#include <cstdlib> +#include <cstring> +#include <ctime> + +#include "cxxstdio.hpp" +//#include "mmo.hpp" #include "utils.hpp" +#include "../poison.hpp" + +static fd_set readfds; -int fd_max; -int currentuse; +int fd_max; +static +int currentuse; +static const uint32_t RFIFO_SIZE = 65536; +static const uint32_t WFIFO_SIZE = 65536; struct socket_data *session[FD_SETSIZE]; +/// clean up by discarding handled bytes +inline +void RFIFOFLUSH(int fd) +{ + memmove(session[fd]->rdata, RFIFOP(fd, 0), RFIFOREST(fd)); + session[fd]->rdata_size = RFIFOREST(fd); + session[fd]->rdata_pos = 0; +} + +/// how much room there is to read more data +inline +size_t RFIFOSPACE(int fd) +{ + return session[fd]->max_rdata - session[fd]->rdata_size; +} + + /// Discard all input -static void null_parse (int fd); +static +void null_parse(int fd); /// Default parser for new connections -static void (*default_func_parse) (int) = null_parse; +static +void(*default_func_parse)(int) = null_parse; -void set_defaultparse (void (*defaultparse) (int)) +void set_defaultparse(void(*defaultparse)(int)) { default_func_parse = defaultparse; } /// Read from socket to the queue -static void recv_to_fifo (int fd) +static +void recv_to_fifo(int fd) { if (session[fd]->eof) return; - ssize_t len = read (fd, session[fd]->rdata + session[fd]->rdata_size, - RFIFOSPACE (fd)); + ssize_t len = read(fd, session[fd]->rdata + session[fd]->rdata_size, + RFIFOSPACE(fd)); if (len > 0) { @@ -58,19 +81,20 @@ static void recv_to_fifo (int fd) } } -static void send_from_fifo (int fd) +static +void send_from_fifo(int fd) { if (session[fd]->eof) return; - ssize_t len = write (fd, session[fd]->wdata, session[fd]->wdata_size); + ssize_t len = write(fd, session[fd]->wdata, session[fd]->wdata_size); if (len > 0) { session[fd]->wdata_size -= len; if (session[fd]->wdata_size) { - memmove (session[fd]->wdata, session[fd]->wdata + len, + memmove(session[fd]->wdata, session[fd]->wdata + len, session[fd]->wdata_size); } session[fd]->connected = 1; @@ -81,32 +105,34 @@ static void send_from_fifo (int fd) } } -static void null_parse (int fd) +static +void null_parse(int fd) { - printf ("null_parse : %d\n", fd); - RFIFOSKIP (fd, RFIFOREST (fd)); + PRINTF("null_parse : %d\n", fd); + RFIFOSKIP(fd, RFIFOREST(fd)); } -static void connect_client (int listen_fd) +static +void connect_client(int listen_fd) { struct sockaddr_in client_address; - socklen_t len = sizeof (client_address); + socklen_t len = sizeof(client_address); - int fd = accept (listen_fd, (struct sockaddr *) &client_address, &len); + int fd = accept(listen_fd, (struct sockaddr *) &client_address, &len); if (fd == -1) { - perror ("accept"); + perror("accept"); return; } if (fd_max <= fd) { fd_max = fd + 1; } - if (!free_fds ()) + if (!free_fds()) { - fprintf (stderr, "softlimit reached, disconnecting : %d\n", fd); - delete_session (fd); + FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd); + delete_session(fd); return; } @@ -114,11 +140,11 @@ static void connect_client (int listen_fd) /// Allow to bind() again after the server restarts. // Since the socket is still in the TIME_WAIT, there's a possibility // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); /// Send packets as soon as possible /// even if the kernel thinks there is too little for it to be worth it! /// Testing shows this is indeed a good idea. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); // Linux-ism: Set socket options to optimize for thin streams // See http://lwn.net/Articles/308919/ and @@ -130,13 +156,13 @@ static void connect_client (int listen_fd) setsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof yes); #endif - FD_SET (fd, &readfds); + FD_SET(fd, &readfds); - fcntl (fd, F_SETFL, O_NONBLOCK); + fcntl(fd, F_SETFL, O_NONBLOCK); - CREATE (session[fd], struct socket_data, 1); - CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); - CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); + CREATE(session[fd], struct socket_data, 1); + CREATE(session[fd]->rdata, uint8_t, RFIFO_SIZE); + CREATE(session[fd]->wdata, uint8_t, WFIFO_SIZE); session[fd]->max_rdata = RFIFO_SIZE; session[fd]->max_wdata = WFIFO_SIZE; @@ -144,72 +170,72 @@ static void connect_client (int listen_fd) session[fd]->func_send = send_from_fifo; session[fd]->func_parse = default_func_parse; session[fd]->client_addr = client_address; - session[fd]->created = time (NULL); + session[fd]->created = TimeT::now(); session[fd]->connected = 0; currentuse++; } -int make_listen_port (uint16_t port) +int make_listen_port(uint16_t port) { struct sockaddr_in server_address; - int fd = socket (AF_INET, SOCK_STREAM, 0); + int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { - perror ("socket"); + perror("socket"); return -1; } if (fd_max <= fd) fd_max = fd + 1; - fcntl (fd, F_SETFL, O_NONBLOCK); + fcntl(fd, F_SETFL, O_NONBLOCK); const int yes = 1; /// Allow to bind() again after the server restarts. // Since the socket is still in the TIME_WAIT, there's a possibility // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); /// Send packets as soon as possible /// even if the kernel thinks there is too little for it to be worth it! // I'm not convinced this is a good idea; although in minimizes the // latency for an individual write, it increases traffic in general. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); server_address.sin_family = AF_INET; - server_address.sin_addr.s_addr = htonl (INADDR_ANY); - server_address.sin_port = htons (port); + server_address.sin_addr.s_addr = htonl(INADDR_ANY); + server_address.sin_port = htons(port); - if (bind (fd, (struct sockaddr *) &server_address, - sizeof (server_address)) == -1) + if (bind(fd, (struct sockaddr *) &server_address, + sizeof(server_address)) == -1) { - perror ("bind"); - exit (1); + perror("bind"); + exit(1); } - if (listen (fd, 5) == -1) + if (listen(fd, 5) == -1) { /* error */ - perror ("listen"); - exit (1); + perror("listen"); + exit(1); } - FD_SET (fd, &readfds); + FD_SET(fd, &readfds); - CREATE (session[fd], struct socket_data, 1); + CREATE(session[fd], struct socket_data, 1); session[fd]->func_recv = connect_client; - session[fd]->created = time (NULL); + session[fd]->created = TimeT::now(); session[fd]->connected = 1; currentuse++; return fd; } -int make_connection (uint32_t ip, uint16_t port) +int make_connection(uint32_t ip, uint16_t port) { struct sockaddr_in server_address; - int fd = socket (AF_INET, SOCK_STREAM, 0); + int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { - perror ("socket"); + perror("socket"); return -1; } if (fd_max <= fd) @@ -219,43 +245,43 @@ int make_connection (uint32_t ip, uint16_t port) /// Allow to bind() again after the server restarts. // Since the socket is still in the TIME_WAIT, there's a possibility // that formerly lost packets might be delivered and confuse the server. - setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); /// Send packets as soon as possible /// even if the kernel thinks there is too little for it to be worth it! // I'm not convinced this is a good idea; although in minimizes the // latency for an individual write, it increases traffic in general. - setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = ip; - server_address.sin_port = htons (port); + server_address.sin_port = htons(port); - fcntl (fd, F_SETFL, O_NONBLOCK); + fcntl(fd, F_SETFL, O_NONBLOCK); /// Errors not caught - we must not block /// Let the main select() loop detect when we know the state - connect (fd, (struct sockaddr *) &server_address, - sizeof (struct sockaddr_in)); + connect(fd, (struct sockaddr *) &server_address, + sizeof(struct sockaddr_in)); - FD_SET (fd, &readfds); + FD_SET(fd, &readfds); - CREATE (session[fd], struct socket_data, 1); - CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE); - CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE); + CREATE(session[fd], struct socket_data, 1); + CREATE(session[fd]->rdata, uint8_t, RFIFO_SIZE); + CREATE(session[fd]->wdata, uint8_t, WFIFO_SIZE); session[fd]->max_rdata = RFIFO_SIZE; session[fd]->max_wdata = WFIFO_SIZE; session[fd]->func_recv = recv_to_fifo; session[fd]->func_send = send_from_fifo; session[fd]->func_parse = default_func_parse; - session[fd]->created = time (NULL); + session[fd]->created = TimeT::now(); session[fd]->connected = 1; currentuse++; return fd; } -void delete_session (int fd) +void delete_session(int fd) { if (fd < 0 || fd >= FD_SETSIZE) return; @@ -264,151 +290,155 @@ void delete_session (int fd) // but this is cheap and good enough for the typical case if (fd == fd_max - 1) fd_max--; - FD_CLR (fd, &readfds); + FD_CLR(fd, &readfds); if (session[fd]) { - free (session[fd]->rdata); - free (session[fd]->wdata); - free (session[fd]->session_data); - free (session[fd]); + free(session[fd]->rdata); + free(session[fd]->wdata); + free(session[fd]->session_data); + free(session[fd]); } session[fd] = NULL; // just close() would try to keep sending buffers - shutdown (fd, SHUT_RDWR); - close (fd); + shutdown(fd, SHUT_RDWR); + close(fd); currentuse--; if (currentuse < 0) { - fprintf (stderr, "delete_session: current sessions negative!\n"); + FPRINTF(stderr, "delete_session: current sessions negative!\n"); currentuse = 0; } return; } -void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size) +void realloc_fifo(int fd, size_t rfifo_size, size_t wfifo_size) { struct socket_data *s = session[fd]; if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size) { - RECREATE (s->rdata, uint8_t, rfifo_size); + RECREATE(s->rdata, uint8_t, rfifo_size); s->max_rdata = rfifo_size; } if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size) { - RECREATE (s->wdata, uint8_t, wfifo_size); + RECREATE(s->wdata, uint8_t, wfifo_size); s->max_wdata = wfifo_size; } } -void WFIFOSET (int fd, size_t len) +void WFIFOSET(int fd, size_t len) { struct socket_data *s = session[fd]; if (s->wdata_size + len + 16384 > s->max_wdata) { - realloc_fifo (fd, s->max_rdata, s->max_wdata << 1); - printf ("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata); + realloc_fifo(fd, s->max_rdata, s->max_wdata << 1); + PRINTF("socket: %d wdata expanded to %zu bytes.\n", fd, s->max_wdata); } if (s->wdata_size + len + 2048 < s->max_wdata) s->wdata_size += len; else - fprintf (stderr, "socket: %d wdata lost !!\n", fd), abort (); + FPRINTF(stderr, "socket: %d wdata lost !!\n", fd), abort(); } -void do_sendrecv (uint32_t next) +void do_sendrecv(interval_t next_ms) { fd_set rfd = readfds, wfd; - FD_ZERO (&wfd); + FD_ZERO(&wfd); for (int i = 0; i < fd_max; i++) { if (session[i] && session[i]->wdata_size) - FD_SET (i, &wfd); + FD_SET(i, &wfd); } struct timeval timeout; - timeout.tv_sec = next / 1000; - timeout.tv_usec = next % 1000 * 1000; - if (select (fd_max, &rfd, &wfd, NULL, &timeout) <= 0) + { + std::chrono::seconds next_s = std::chrono::duration_cast<std::chrono::seconds>(next_ms); + std::chrono::microseconds next_us = next_ms - next_s; + timeout.tv_sec = next_s.count(); + timeout.tv_usec = next_us.count(); + } + if (select(fd_max, &rfd, &wfd, NULL, &timeout) <= 0) return; for (int i = 0; i < fd_max; i++) { if (!session[i]) continue; - if (FD_ISSET (i, &wfd)) + if (FD_ISSET(i, &wfd)) { if (session[i]->func_send) //send_from_fifo(i); - session[i]->func_send (i); + session[i]->func_send(i); } - if (FD_ISSET (i, &rfd)) + if (FD_ISSET(i, &rfd)) { if (session[i]->func_recv) //recv_to_fifo(i); //or connect_client(i); - session[i]->func_recv (i); + session[i]->func_recv(i); } } } -void do_parsepacket (void) +void do_parsepacket(void) { for (int i = 0; i < fd_max; i++) { if (!session[i]) continue; if (!session[i]->connected - && time (NULL) - session[i]->created > CONNECT_TIMEOUT) + && static_cast<time_t>(TimeT::now()) - static_cast<time_t>(session[i]->created) > CONNECT_TIMEOUT) { - printf ("Session #%d timed out\n", i); + PRINTF("Session #%d timed out\n", i); session[i]->eof = 1; } if (!session[i]->rdata_size && !session[i]->eof) continue; if (session[i]->func_parse) { - session[i]->func_parse (i); + session[i]->func_parse(i); /// some func_parse may call delete_session if (!session[i]) continue; } /// Reclaim buffer space for what was read - RFIFOFLUSH (i); + RFIFOFLUSH(i); } } -void do_socket (void) +void do_socket(void) { - FD_ZERO (&readfds); + FD_ZERO(&readfds); currentuse = 3; } -void RFIFOSKIP (int fd, size_t len) +void RFIFOSKIP(int fd, size_t len) { struct socket_data *s = session[fd]; s->rdata_pos += len; if (s->rdata_size < s->rdata_pos) { - fprintf (stderr, "too many skip\n"); - abort (); + FPRINTF(stderr, "too many skip\n"); + abort(); } } -void fclose_ (FILE * fp) +void fclose_(FILE * fp) { - if (fclose (fp)) - perror ("fclose"), abort (); + if (fclose(fp)) + perror("fclose"), abort(); currentuse--; } -FILE *fopen_ (const char *path, const char *mode) +FILE *fopen_(const char *path, const char *mode) { - FILE *f = fopen (path, mode); + FILE *f = fopen(path, mode); if (f) currentuse++; return f; } -bool free_fds (void) +bool free_fds(void) { return currentuse < SOFT_LIMIT; } |