diff options
Diffstat (limited to 'socket_server.c')
| -rwxr-xr-x | socket_server.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/socket_server.c b/socket_server.c new file mode 100755 index 0000000..9281595 --- /dev/null +++ b/socket_server.c @@ -0,0 +1,251 @@ +#include <netinet/in.h> +#include <poll.h> +#include <pthread.h> +#include <socket_server.h> +#include <stdatomic.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +struct Server server_constructor(int domain, int service, int protocol, ulong interface, int port, int backlog) +{ + struct Server server; + + server.domain = domain; + server.service = service; + server.protocol = protocol; + server.interface = interface; + server.port = port; + server.backlog = backlog; + + server.address.sin_family = domain; + server.address.sin_port = htons(port); + server.address.sin_addr.s_addr = htonl(interface); + + server.socket = socket(domain, service, protocol); + + if (server.socket == 0) { + perror("failed to connect to socket...\n"); + exit(1); + } + + if (bind(server.socket, (struct sockaddr*)&server.address, sizeof(server.address)) < 0) { + perror("failed to bind socket...\n"); + exit(1); + } + + if (listen(server.socket, server.backlog) < 0) { + perror("failed to start listening...\n"); + exit(1); + } + + server.accepted_socket = NULL; + + server.on_message = NULL; + server.on_disconnect = NULL; + + server.running = true; + + return server; +}; + +struct recv_data_arg { + struct Server* server; + struct AcceptedSocket* curr_socket; +}; + +void* recv_data(void* threadarg) +{ + struct recv_data_arg* recv_d_arg; + recv_d_arg = (struct recv_data_arg*)threadarg; + + while (recv_d_arg->server->running) { + char buffer[30000] = { 0 }; + ssize_t amountRecv = recv(recv_d_arg->curr_socket->socketFD, buffer, 30000, 0); + + if (recv_d_arg->server->running && recv_d_arg->curr_socket->shutdown == false) { + if (amountRecv == 0) + break; + + if (recv_d_arg->server->on_message) + recv_d_arg->server->on_message(recv_d_arg->server, recv_d_arg->curr_socket, buffer, amountRecv); + } + } + + pthread_rwlock_wrlock(&recv_d_arg->server->list_lock); + close(recv_d_arg->curr_socket->socketFD); + recv_d_arg->curr_socket->shutdown = true; + pthread_rwlock_unlock(&recv_d_arg->server->list_lock); + + if (recv_d_arg->server->on_disconnect) + recv_d_arg->server->on_disconnect(recv_d_arg->server, recv_d_arg->curr_socket); + + free(recv_d_arg); + pthread_exit(NULL); +} + +void cleanup_conns(struct Server* server) +{ + pthread_rwlock_wrlock(&server->list_lock); + if (server->accepted_socket) { + struct AcceptedSocket* sel_socket = server->accepted_socket; + struct AcceptedSocket* prev_socket = server->accepted_socket; + struct AcceptedSocket* prev2_socket = server->accepted_socket; + + while (sel_socket != NULL) { + sel_socket = sel_socket->next; + if (prev_socket->shutdown == true) { + if (prev_socket == server->accepted_socket) { + pthread_join(server->accepted_socket->thread_id, NULL); + free(server->accepted_socket); + server->accepted_socket = sel_socket; + } else { + pthread_join(prev2_socket->next->thread_id, NULL); + free(prev2_socket->next); + prev2_socket->next = sel_socket; + } + } else { + prev2_socket = prev_socket; + } + prev_socket = sel_socket; + } + } + pthread_rwlock_unlock(&server->list_lock); +} + +void* cleanup_loop(void* threadarg) +{ + struct Server* server; + server = (struct Server*)threadarg; + while (server->running) { + cleanup_conns(server); + for (int i = 0; i < 60 && server->running; i++) { + sleep(1); + } + } + + pthread_exit(NULL); +} + +struct AcceptedSocket* acceptIncoming(struct Server* server) +{ + int serverSocketFd = server->socket; + + struct sockaddr_in clientAddress; + socklen_t ClientAddressSize = sizeof(struct sockaddr); + int clientSocketFD = accept(serverSocketFd, (struct sockaddr*)&clientAddress, &ClientAddressSize); + + if (server->running && clientSocketFD > 0) { + struct AcceptedSocket* acceptedSocket = malloc(sizeof(struct AcceptedSocket)); + if (!acceptedSocket) + return NULL; + acceptedSocket->address = clientAddress; + acceptedSocket->socketFD = clientSocketFD; + acceptedSocket->next = NULL; + acceptedSocket->shutdown = false; + + if (server->running) { + pthread_rwlock_wrlock(&server->list_lock); + struct AcceptedSocket* sel_socket = server->accepted_socket; + + if (sel_socket == NULL) { + server->accepted_socket = acceptedSocket; + } else { + while (1) { + if (sel_socket->next == NULL) { + sel_socket->next = acceptedSocket; + break; + } else { + sel_socket = sel_socket->next; + } + } + } + pthread_rwlock_unlock(&server->list_lock); + } + return acceptedSocket; + } else { + return NULL; + } +} + +void* await_client(void* threadarg) +{ + struct Server* server; + server = (struct Server*)threadarg; + while (server->running) { + struct AcceptedSocket* new_socket = acceptIncoming(server); + if (server->running && new_socket) { + struct recv_data_arg* recv_d_arg = malloc(sizeof(*recv_d_arg)); + recv_d_arg->server = server; + recv_d_arg->curr_socket = new_socket; + + int thread_err = pthread_create(&new_socket->thread_id, NULL, recv_data, recv_d_arg); + if (thread_err != 0) { + fprintf(stderr, "ERROR CREATING THREAD FOR CLIENT: %d\n", thread_err); + perror("await client error"); + exit(-1); + } + } + } + + pthread_exit(NULL); +} + +int server_close_socket(struct Server* server) +{ + server->running = false; + pthread_join(server->cleanup_thread_id, NULL); + + int shuterr = shutdown(server->socket, SHUT_RDWR); + pthread_join(server->server_thread_id, NULL); + int closeerr = close(server->socket); + + cleanup_conns(server); + pthread_rwlock_destroy(&server->list_lock); + return shuterr + closeerr; +} + +int init_socket_server(struct Server* server) +{ + pthread_rwlock_init(&server->list_lock, NULL); + + int thread_err2 = pthread_create(&server->cleanup_thread_id, NULL, cleanup_loop, (void*)server); + if (thread_err2 != 0) { + fprintf(stderr, "ERROR WITH CLEANUP LOOP: %d\n", thread_err2); + perror("cleanup loop err"); + exit(-1); + } + + int thread_err = pthread_create(&server->server_thread_id, NULL, await_client, (void*)server); + if (thread_err != 0) { + fprintf(stderr, "ERROR WITH AWAIT CLIENT: %d\n", thread_err); + perror("await client error"); + exit(-1); + } + + return 0; +} + +void soc_send(struct Server* server, struct AcceptedSocket* cur_socket, char* message) +{ + pthread_rwlock_rdlock(&server->list_lock); + if(cur_socket->shutdown==false) + send(cur_socket->socketFD, message, 30000, MSG_NOSIGNAL); + pthread_rwlock_unlock(&server->list_lock); +} + +void soc_broadcast(struct Server* server, struct AcceptedSocket* cur_socket, char* message) +{ + pthread_rwlock_rdlock(&server->list_lock); + struct AcceptedSocket* sel_socket = server->accepted_socket; + while (sel_socket != NULL) { + if (sel_socket->shutdown == false && sel_socket->socketFD != cur_socket->socketFD) { + soc_send(server, sel_socket, message); + } + sel_socket = sel_socket->next; + } + pthread_rwlock_unlock(&server->list_lock); +}
\ No newline at end of file |
