From fc517f9113b012a9872dcb7938846bcc94a5fd4d Mon Sep 17 00:00:00 2001 From: Gediminas Jakutis Date: Sat, 5 May 2018 23:36:10 +0300 Subject: server: separate networking code into its own thread[s]. --- src/server/main.c | 22 ++++--- src/server/net.c | 193 +++++++++++++++++++++++++++++++++++++----------------- src/server/net.h | 9 ++- 3 files changed, 151 insertions(+), 73 deletions(-) diff --git a/src/server/main.c b/src/server/main.c index 5c0628a..f4d9fdc 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -33,22 +33,26 @@ #include #include "net.h" -static struct netstate *init(const unsigned int port); +int init(const unsigned short int port); static void draw_idle(void); static void draw_busy(const char * const data); static float digest_temp(const short int rawdata); int main(void) { - struct netstate *state; - char data[32] = {0}; + static const struct timespec wait = {0, 200 * 1000 * 1000}; /* 200 ms */ + char data[dgsize] = {0}; int status; - struct timespec wait = {0, 100 * 1000 * 1000}; + int nd; - state = init(2191); + nd = init(2191); + + if (nd == NET_ERROR) { + goto fail; + } do { - status = state->getlastdata(data, state); + status = net_getlastdata(nd, data); switch (status) { case NET_OK: /*fall-through */ @@ -65,15 +69,15 @@ int main(void) } while (getch() != 'q'); fail: - net_close(&state); + net_close(nd); endwin(); return 0; } -static struct netstate *init(const unsigned int port) +int init(const unsigned short int port) { - struct netstate *ret; + int ret; ret = net_init(port); initscr(); diff --git a/src/server/net.c b/src/server/net.c index 1325da0..100ba9c 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -24,96 +24,177 @@ #include #include #include -#include +#include #include +#include #include +#include #include "net.h" -struct netstate_internal { - int (*getlastdata)(char * const data, struct netstate * const state); - unsigned int handle; +struct netstate { struct timespec lastreply; + int nd; int sock; + unsigned short int port; + pthread_t listner; + char *data; + size_t bufsize; + unsigned int available; + int status; + pthread_mutex_t datamutex; }; -static int dolisten(char * const data, struct netstate_internal * const state); +static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; +static struct netstate *state; +static ssize_t count; +static size_t available_count; + +static void *dolisten(void * state); static int getpacket(char *data, size_t buffsize, ssize_t *recvbufsize, int sock, struct sockaddr_in *sender); -static int getlastdata_internal(char * const data, struct netstate * const state); -struct netstate *net_init(const unsigned int port) +int net_init(const unsigned short int port) { - struct netstate_internal *ret; struct sockaddr_in bindaddress = {0}; + int ret; + ssize_t i; + int sizechanged = 0; + + pthread_mutex_lock(&initmutex); + if (available_count) { + for (i = 0; !state[i].available; ++i) { + if (i == count) { + ret = NET_ERROR; + goto out; + } + } + } else { + if (++count == INT_MAX) { + ret = NET_ERROR; + goto out; + } - ret = malloc(sizeof(struct netstate_internal)); + i = count - 1; + state = realloc(state, sizeof(struct netstate) * count); + sizechanged = 1; + } + state[i].available = 0; bindaddress.sin_family = AF_INET; bindaddress.sin_port = htons(port); - ret->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (bind(ret->sock, (struct sockaddr*) &bindaddress, sizeof(bindaddress))) { - free(ret); - return NULL; + state[i].sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (bind(state[i].sock, (struct sockaddr*) &bindaddress, sizeof(bindaddress))) { + ret = NET_ERROR; + goto out; + } + + clock_gettime(CLOCK_MONOTONIC_RAW, &state[i].lastreply); + state[i].lastreply.tv_sec -= 5; + state[i].nd = i + 1; + state[i].port = port; + state[i].data = malloc(dgsize); + memset(state[i].data, 0, dgsize); + state[i].bufsize = dgsize; + state[i].status = NET_NONEWDATA; + pthread_mutex_init(&state[i].datamutex, NULL); + + if (pthread_create(&state[i].listner, NULL, dolisten, state + i)) { + ret = NET_ERROR; + goto out; } - clock_gettime(CLOCK_MONOTONIC_RAW, &ret->lastreply); - ret->lastreply.tv_sec -= 5; - ret->handle = 0; /* currently unused */ - ret->getlastdata = &getlastdata_internal; + ret = count; +out: + if (ret == NET_ERROR && sizechanged) { + state = realloc(state, sizeof(struct netstate) * --count); + } - return (struct netstate *) ret; + pthread_mutex_unlock(&initmutex); + return ret; } -int net_close(struct netstate **state) { - close((*(struct netstate_internal **)state)->sock); - free(*state); - *state = NULL; - return 0; +int net_close(int nd) +{ + int ret; + + pthread_mutex_lock(&initmutex); + if (nd > count || nd < 1 || state[nd - 1].available) { + ret = NET_ERROR; + } else { + --nd; + pthread_cancel(state[nd].listner); + pthread_join(state[nd].listner, NULL); + close(state[nd].sock); + free(state[nd].data); + state[nd].available = 1; + ret = NET_OK; + } + + pthread_mutex_unlock(&initmutex); + return ret; +} + +int net_getlastdata(int nd, char * const data) +{ + int ret; + + if (nd > count || nd < 1 || state[--nd].available) { + ret = NET_ERROR; + } else if (!(ret = state[nd].status)) { + memcpy(data, state[nd].data, dgsize); + } + + return ret; } -static int dolisten(char * const data, struct netstate_internal * const state) +static void *dolisten(void * state) { - char buff[9001] = {0}; static const char servermagic[] = "I love coffee!"; static const char clientmagic[] = "I love tea!"; struct timespec now; - struct timespec wait = {0, 25 * 1000 * 1000}; + struct timespec wait = {0, 10 * 1000 * 1000}; /* 10 ms */ struct sockaddr_in clientaddr; - size_t i = 10; + struct netstate *st; ssize_t recvbufsize; - int ret; + int cancelstate; + + st = state; do { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate); + pthread_mutex_lock(&st->datamutex); clock_gettime(CLOCK_MONOTONIC_RAW, &now); - ret = getpacket(buff, sizeof(buff), &recvbufsize, state->sock, &clientaddr); - - switch (ret) { - case NET_OK: - state->lastreply = now; - if (buff[0] == 'I') { - if (!(strcmp(buff, servermagic))) { - sendto(state->sock, clientmagic, sizeof(clientmagic), - MSG_DONTWAIT, (struct sockaddr *) &clientaddr, sizeof(clientaddr)); - } - ret = NET_NONEWDATA; /* consume packet and lie about it */ - continue; - } else { - memcpy(data, buff, recvbufsize); + st->status = getpacket(st->data, st->bufsize, &recvbufsize, st->sock, &clientaddr); + + if (!st->status) { + st->lastreply = now; + if (st->data[0] == 'I' && !(strcmp(st->data, servermagic))) { + sendto(st->sock, clientmagic, sizeof(clientmagic), + MSG_DONTWAIT, (struct sockaddr *) &clientaddr, sizeof(clientaddr)); + st->status = NET_NONEWDATA; /* consume packet and lie about it */ } - break; - default: - ; /* noop */ } - if (!ret) { - nanosleep(&wait, NULL); + /* no packets in five seconds */ + if ((now.tv_sec - st->lastreply.tv_sec) >= 5) { + st->status = NET_DEAD; + } else if (st->data[0] != 'I') { + /* we don't actually want to have NONEWDATA set from this loop, + * barring the one exeption that is after arrival of the beacon + * packet, as it'd keep the value at NONEWDATA almost constantly + * due to polling being generally much more frequent than the + * actual packet rate. + */ + st->status = NET_OK; } - } while (--i && ret != 0); - /* no packets in five seconds */ - if ((now.tv_sec - state->lastreply.tv_sec) >= 5) { - ret = NET_DEAD; - } + pthread_mutex_unlock(&st->datamutex); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &cancelstate); + pthread_testcancel(); - return ret; + nanosleep(&wait, NULL); + + } while (1); + + return NULL; } static int getpacket(char *data, size_t buffsize, ssize_t *recvbufsize, int sock, struct sockaddr_in *sender) @@ -142,9 +223,3 @@ static int getpacket(char *data, size_t buffsize, ssize_t *recvbufsize, int sock return ret; } - - -static int getlastdata_internal(char * const data, struct netstate * const state) -{ - return dolisten(data, (struct netstate_internal *) state); -} diff --git a/src/server/net.h b/src/server/net.h index 57c17f6..92ba962 100644 --- a/src/server/net.h +++ b/src/server/net.h @@ -26,11 +26,10 @@ #define NET_NONEWDATA 1 #define NET_DEAD 2 -struct netstate { - int (*getlastdata)(char * const data, struct netstate * const state); -}; +#define dgsize 512 -struct netstate *net_init(const unsigned int port); -int net_close(struct netstate **state); +int net_init(const unsigned short int port); +int net_close(int nd); +int net_getlastdata(int nd, char * const data); #endif /* NET_H_INCLUDED */ -- cgit v1.2.3