#include #include #include #include #include #include #include #include #include #include #include "chatlib.h" // A global that is a list of clients struct clinfo** client_list; size_t client_list_size = 0; size_t client_list_mem; pthread_mutex_t cl_mutex = PTHREAD_MUTEX_INITIALIZER; int skt; void stop_server(int signum){ close(skt); if(signum == SIGSEGV) printf("Received signal 11, SIGSEGV!\n"); exit(0); } void pipe_fn(int signal){ time_t now = time(0); printf("Broken pipe at %s\n", ctime(&now)); } struct clinfo { char ipaddr[16]; int skt; char name[32]; }; int find_username(char* uname){ for(size_t i = 0; i < client_list_size; i++) if(!strncmp(client_list[i]->name, uname, 32)) return client_list[i]->skt; return 0; } #define BUFSIZE (1024*64) void* client_thread(void* param){ struct clinfo *ctp = (struct clinfo*)param; char buffer[BUFSIZE]; int cfd = ctp->skt; size_t readlen; /* * Handle Joining the Chat * We need to receive a unique name, and not move on until then! * for(;;){ * Ask the client for a name * if the name isn't taken, add them to the list of names and break out of the loop * } */ for(;;){ if(read_message(cfd, buffer, BUFSIZE)) goto bad_problem; if(buffer[0] != USER) goto bad_problem; if(!find_username(buffer + 1)) { strncpy(ctp->name, buffer+1, 32); break; } else { char four = NAMETAKEN; write(cfd, &four, 1); } printf("Trying again on the username, can't use %s\n", buffer+1); } printf("Client at %s has name %s\n", ctp->ipaddr, ctp->name); pthread_mutex_lock(&cl_mutex); for(size_t i = 0; i < client_list_size; i++) write(client_list[i]->skt, buffer, 33); pthread_mutex_unlock(&cl_mutex); // Handle interaction with one particular client for(;;){ if(read_message(cfd, buffer, BUFSIZE)) goto bad_problem; if(buffer[0] == TEXT){ printf("%s (%s): %s\n", ctp->name, ctp->ipaddr, buffer+35); // Relay the message to everybody pthread_mutex_lock(&cl_mutex); for(size_t i = 0; i < client_list_size; i++){ printf("Relay to %s %s\n", ctp->name, ctp->ipaddr); send_message(client_list[i]->skt, ctp->name, buffer+35); } pthread_mutex_unlock(&cl_mutex); } else if(buffer[0] == PM){ printf("PM to %s from %s: %s\n", buffer+3, buffer+35, buffer+67); int oskt; pthread_mutex_lock(&cl_mutex); if(oskt = find_username(buffer+3)) write(oskt, buffer, 67+ *(uint16_t*)(buffer+1)); else printf("Could not find client %s (find_username returned %d)\n", buffer+3, oskt); pthread_mutex_lock(&cl_mutex); } else goto bad_problem; } // If the client disconnects, we need to deal with that! // Send a message to the other client that this client has left bad_problem: printf("Bad problem with client %s, ip %s\n", ctp->name, ctp->ipaddr); done: pthread_mutex_lock(&cl_mutex); close(cfd); // Note: This is NOT thread safe // It should be! // At some point, probably closer to server time, we'll fix it size_t clpos = 0; for(; clpos < client_list_size && client_list[clpos] != ctp; clpos++); client_list[clpos] = client_list[--client_list_size]; pthread_mutex_unlock(&cl_mutex); printf("All done with %s, client_list_size == %lu\n", ctp->name, client_list_size); free(ctp); return 0; } int main(int argc, char ** argv){ struct sockaddr_in sad; uint16_t port = 5141; if(argc > 1) port = atoi(argv[1]); sad.sin_port = htons(port); sad.sin_addr.s_addr = INADDR_ANY; sad.sin_family = AF_INET; skt = socket(AF_INET, SOCK_STREAM, 0); struct sigaction handler; handler.sa_handler = stop_server; sigaction(SIGINT, &handler, 0); sigaction(SIGTERM, &handler, 0); sigaction(SIGSEGV, &handler, 0); struct sigaction pipe_handler; pipe_handler.sa_handler = pipe_fn; sigaction(SIGPIPE, &pipe_handler, 0); bind(skt, (struct sockaddr *)(&sad), sizeof(struct sockaddr_in)); listen(skt, 5); int client_fd; struct sockaddr_in client_addr; client_list = (struct clinfo**)malloc(sizeof(void*)*1024); client_list_mem = 1024; for(;;){ socklen_t client_address_length = sizeof(struct sockaddr); client_fd = accept(skt, (struct sockaddr*)&client_addr, &client_address_length); pthread_mutex_lock(&cl_mutex); printf("Accepted a connection from %s\n", inet_ntoa(client_addr.sin_addr)); if(client_list_size >= client_list_mem){ client_list = (struct clinfo**)realloc(client_list, sizeof(void*) * (client_list_mem + 1024)); client_list_mem += 1024; } // Start a thread to talk to that client pthread_t thread; struct clinfo *ctp = (struct clinfo*)calloc(sizeof(struct clinfo), 1); ctp->skt = client_fd; client_list[client_list_size++] = ctp; pthread_mutex_unlock(&cl_mutex); strncpy(ctp->ipaddr, inet_ntoa(client_addr.sin_addr), 16); pthread_create(&thread, 0, client_thread, ctp); } close(skt); return 0; }