/* * This is a better server than simple_server.c * I'm only going to comment changes from simple_server.c * So if there's something that doesn't make any sense in here, maybe look at that one. */ #include #include #include #include #include #include #include #include #include int skt; /* This data block enforces a hard limit of 128 clients, ever. * If we hit 128 clients, we need to restart the server. * We could work out a better system with realloc or something, but didn't. * * We're going to need to relay messages to all connected clients, * so we'll need a list of client file descriptors. That's what * this is for. */ #define MAX_CLIENTS 128 size_t current_clients = 0; int client_fds[MAX_CLIENTS]; void shut_down_server(int signal){ printf("\nDoing everything we need to do to shut down the server\n"); close(skt); exit(0); } /* sigpipe is generated when we write to a disconnected network socket. * sometimes, at least. Other times, it seems like write just returns -1. * sigpipe will crash the server if we don't handle it. * * You might think we should be able to figure out what client file descriptor * caused the sigpipe. There's actually not a good way to do this inside the * signal handler. It's easy when we call write, though, since it'll return * -1 if write didn't work. So we can just figure it out when we're calling write. */ void handle_sigpipe(int signal){ printf("Received a sigpipe\n"); } void handle_client(size_t cfd_index){ int client_fd = client_fds[cfd_index]; write(client_fd, "Good Morning\n", 14); char buffer[1024]; ssize_t len; for(;;){ len = read(client_fd, buffer, 1024); if(len < 1) break; // write to everybody, not just ourselves! // for each client which is connected: // write to that client for(int i = 0; i < current_clients; i++) if(client_fds[i] != -1) if(write(client_fds[i], buffer, len) < 1) // if write returns an error... client_fds[i] = -1; // mark the file descriptor as "not working" if(len < 1) break; } printf("Done with client %d\n", cfd_index); client_fds[cfd_index] = -1; // In this case, we're also done with us close(client_fd); } int main(int argc, char ** argv){ uint16_t listen_port = 5141; // 5141 is the default if(argc > 1){ listen_port = atoi(argv[1]); if(listen_port < 1){ printf("Invalid port: %s\n", argv[1]); exit(1); } printf("Will listen on port %d\n", listen_port); } else { printf("No port specified, defaulting to %d\n", listen_port); } struct sigaction sa; sa.sa_handler = shut_down_server; sigaction(SIGINT, &sa, 0); struct sigaction sapipe; sapipe.sa_handler = handle_sigpipe; sigaction(SIGPIPE, &sapipe, 0); struct sockaddr_in sad; sad.sin_port = htons(listen_port); sad.sin_addr.s_addr = INADDR_ANY; sad.sin_family = AF_INET; skt = socket(AF_INET, SOCK_STREAM, 0); // Step 1 if(skt == -1){ perror("socket"); return 1; } if( bind(skt, (struct sockaddr *)(&sad), sizeof(struct sockaddr_in)) ){ // step 2 perror("bind"); return 1; } if( listen(skt, 5) ){ // step 3 perror("listen"); return 1; } int client_fd; struct sockaddr_in client_address; socklen_t address_size = sizeof(struct sockaddr_in); while(1){ client_fd = accept(skt, (struct sockaddr *)(&client_address), &address_size); // step 4 printf("Connection made from address %s\n", inet_ntoa(client_address.sin_addr)); client_fds[current_clients] = client_fd; // start this in a new thread, instead of holding up the main loop right here pthread_t t; /* pthread_create expects the function to return a void*, and take a void* * Ours returns void, and takes a size_t. But we can just do a static cast * on the function pointer and fix it. void* is a memory address, which is a * 64-bit unsigned int just like a size_t, so we can also just cast that. If you * don't like all this casting, maybe you'd prefer the C++ version */ pthread_create(&t, 0, (void*(*)(void*))handle_client, (void*)current_clients); current_clients++; } return 0; }