#include #include #include #include #include #include #include #include #include "shared.h" #define MAX_CLIENTS 100 #define BUFFER_SIZE 1024 struct { int sockets[MAX_CLIENTS]; int count; pthread_mutex_t lock; } clients = {0}; typedef struct { int sock; struct sockaddr_in6 addr; } pthread_input_t; #define tell_everyone(...) do { \ pthread_mutex_lock(&clients.lock); \ printf(__VA_ARGS__); \ for (int i = 0; i < clients.count; i++) { \ if (dprintf(clients.sockets[i], __VA_ARGS__) < 0) { \ close(clients.sockets[i]); \ clients.sockets[i] = clients.sockets[--clients.count]; \ i--; \ } \ } \ pthread_mutex_unlock(&clients.lock); \ fflush(stdout); \ } while (0) /* const char* ansi_colors[] = { "\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", "\033[37m", "\033[90m", "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", "\033[96m", "\033[97m", "\033[0m" }; static unsigned long djb2(const unsigned char *str, size_t size) { unsigned long hash = 5381; while (size--) hash = ((hash << 5) + hash) + *str++; return hash; } const char* sockaddr_color(const struct sockaddr *addr) { if (addr->sa_family == AF_INET) { return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in*)&addr)->sin_addr, 4) % 16]; } if (addr->sa_family == AF_INET6) { return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in6*)&addr)->sin6_addr, 16) % 16]; } return ansi_colors[16]; }*/ void* handle_client(void* _arg) { const pthread_input_t* arg = _arg; const int client_fd = arg->sock; const struct sockaddr_in6 addr = arg->addr; free(_arg); char message[BUFFER_SIZE]; FILE* reader = fdopen(client_fd, "r"); if (reader == NULL) { perror("fdopen"); close(client_fd); return NULL; } while (fgets(message, BUFFER_SIZE, reader) != NULL) { message[strcspn(message, "\n")] = 0; if (message[0] == '\0') continue; char client_ip[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip)); tell_everyone("%s: %s\n", client_ip, message); } pthread_mutex_lock(&clients.lock); for (int i = 0; i < clients.count; i++) { if (clients.sockets[i] != client_fd) continue; close(clients.sockets[i]); clients.sockets[i] = clients.sockets[--clients.count]; break; } pthread_mutex_unlock(&clients.lock); char client_ip[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip)); tell_everyone("- %s. total clients: %d\n", client_ip, clients.count); return NULL; } int main(int argc, char* argv[]) { struct in6_addr bind_addr = in6addr_any; int port = 5000; for (int i = 1; i < argc; i++) { if (streq(argv[i], "--addr") && i + 1 < argc) { if (inet_pton(AF_INET6, argv[++i], &bind_addr) != 1) { struct in_addr v4; if (inet_pton(AF_INET, argv[i], &v4) != 1) exit_error("invalid address: %s\n", argv[i]); memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.s6_addr[10] = 0xff; bind_addr.s6_addr[11] = 0xff; memcpy(&bind_addr.s6_addr[12], &v4, 4); } } else { port = atoi(argv[i]); if (port <= 0) exit_error("unknown argument: %s\n", argv[i]); } } const int server_fd = socket(AF_INET6, SOCK_STREAM, 0); if (server_fd < 0) exit_error("socket"); int opt = 0; setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in6 server_addr = { .sin6_family = AF_INET6, .sin6_addr = bind_addr, .sin6_port = htons(port) }; if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) exit_perror("bind"); if (listen(server_fd, 128) < 0) exit_perror("listen"); tell_everyone("chatserver successfully started at port %d\n", port); pthread_mutex_init(&clients.lock, NULL); for (;;) { struct sockaddr_in6 client_addr; socklen_t client_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { close(client_fd); continue; } pthread_mutex_lock(&clients.lock); if (clients.count < MAX_CLIENTS) clients.sockets[clients.count++] = client_fd; pthread_input_t* pi = malloc(sizeof(pthread_input_t)); pi->sock = client_fd; pi->addr = client_addr; pthread_t thread; pthread_create(&thread, NULL, handle_client, pi); pthread_detach(thread); pthread_mutex_unlock(&clients.lock); char ip_buf[CC_ADDRLEN]; sockaddr2str((struct sockaddr*)&client_addr, ip_buf, sizeof(ip_buf)); tell_everyone("+ %s. total clients: %d\n", ip_buf, clients.count); } }