aboutsummaryrefslogtreecommitdiff
path: root/socket_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'socket_server.c')
-rwxr-xr-xsocket_server.c251
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