#include #include #include #include #include #include #include #include #include #include #include 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); }