/* hello_server_concurrent.c Adapted from Douglas Comer's "Internetworking with TCP/IP" by Jeff Ondich and Lauren Jantz, summer 1995. Rewritten in C++, Jeff Ondich, January 2000. Rewritten again in C, Jeff Ondich, March 2023. This program is a concurrent server for a tiny "hello world" sort of protocol. Once a TCP connection request arrives, this server accepts the connection and creates a child process to respond to the request. The child sends a message to the client (without null termination), and then closes the connection. See hello_client.c for more details. */ #include #include #include #include #include #include #include "tcp_utilities.h" void log_connection(int socket_descriptor, const char *server_name); void process_request(int socket_descriptor, const char *message); int main(int argc, char **argv) { // Parse the command line. const char *server_name = argv[0]; if (argc != 3) { fprintf(stderr, "Usage: %s port message\n", server_name); fprintf(stderr, "For example: %s 5000 \"Hi there, client!\"\n", server_name); return 1; } int port; if (sscanf(argv[1], "%d", &port) != 1) { fprintf(stderr, "port \"%s\" is not a decimal integer\n", argv[1]); return 1; } const char *message = argv[2]; // Start listening at the specified port. int main_socket = prepare_to_accept_connections(port); fprintf(stderr, "%s is ready to serve at port %d.\n", argv[0], port); // Accept and process connections until the world ends. while (1) { // Answer the phone. Note that accept() makes a duplicate of // the original socket. After the parent process creates a // child process, the child has no use for the original socket // (which is being used to listen for connection requests) and // the parent has no use for the new socket, which will be used // by the child to process the connection request. struct sockaddr_in peer_address; socklen_t address_length = sizeof(peer_address); int new_socket = accept(main_socket, (struct sockaddr *)&peer_address, &address_length); if (new_socket < 0) { fprintf(stderr, "%s: accept failed: %s\n", argv[0], strerror(errno)); exit(1); } // Create a child process to process the request so the parent can // go on listening for new requests. if (fork() != 0) { // Note that after closing the new socket, the parent goes back // to the top of the loop to accept the next connection request. close(new_socket); } else { close(main_socket); log_connection(new_socket, server_name); process_request(new_socket, message); close(new_socket); exit(0); } } return 0; } // Reports information about the current connection request. // This could, of course, be more sophisticated than printing a // message to standard error, but it's not. void log_connection(int socket_descriptor, const char *server_name) { const int buffer_size = 100; char peer_name[buffer_size]; get_peer_host_name(socket_descriptor, peer_name, buffer_size); fprintf(stderr, "%s: processed request from %s\n", server_name, peer_name); } // Implements this server's protocol. Since this server's protocol is pretty // darned trivial, there's not much here. void process_request(int socket_descriptor, const char *message) { // Send the message. Note that writing to a socket is simpler than // reading from one, since you can write everything at once, while reading // depends on when the pieces of the incoming message arrive via the network. if (write(socket_descriptor, message, strlen(message)) < 0) { fprintf(stderr, "An error occurred while writing the message: %s\n", strerror(errno)); } }