/* multiserver.c - Simple TCP server that accepts connections * and forks up to MAXCHILD children to handle them. Each child * redirects the connection to standard input and output and * execs an executable image. If a connection cannot be * accepted, since there are already enough children, * the connection is dropped. * Syntax: multiserver [port [filename]] * port:protocol port number to use * filename: image to execute in child * Note: The port argument is optional. If no protocol port is * specified, the server uses the default given by PROTOPORT(5194). * and the same for filename (default is "child"). */ #include #include #include #include #include #include #include #include #include #include #include #include #include extern int errno; /* Code executed by the child process: after redirecting the socket sd to standard input and output, executes the filename image. */ void child (int sd, const char *filename ) { /* Redirect standard input and standard output to this socket and close the socket */ if ((dup2(sd, STDIN_FILENO) != STDIN_FILENO) || (dup2(sd, STDOUT_FILENO) != STDOUT_FILENO)) { perror("Cannot dup2 to standard files"); exit(0); } close(sd); /* At last we execute the image */ execv(filename, NULL); /* We should never get here */ perror("Failed in signal handler"); exit(1); } #define PROTOPORT 5194 /* default protocol port number*/ #define MAXCHILD 5 /* maximum number of concurrent children */ /* Handler for SIGCHLD signal */ static int children_count = 0; /* Number of children currently running */ void sig_child (int n) { int pid, status; /* The child has terminated. Collect status information */ /* When there is the SIGCHD signal the parent process collects information about all currently deceased children. WNOHANG makes waitpid non-blocking */ while ( (pid = waitpid(-1, &status, WNOHANG)) > 0) children_count--; } int main(int argc, char *argv[]) { struct sockaddr_in sad;/* structure to hold server's address*/ struct sockaddr_in cad;/* structure to hold client's address*/ int sd; /* listening socket descriptor */ int sd2; /* connected socket's descriptor */ int port; /* protocol port number */ int alen; /* length of address */ pid_t pid; /* process id of child process */ int option_value; /* needed for setsockopt */ static char *filename; /* name of file executed by child process*/ /* Check command-line arguments. Use defaults if necessary. */ filename = (argc > 2)?argv[2]:"child"; port = (argc > 1)?atoi(argv[1]):PROTOPORT; /* Create listening socket */ if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(1); } /* clear and set server's sockaddr structure */ memset((char *)&sad, 0, sizeof(sad)); sad.sin_family = AF_INET; /* set family to Internet */ sad.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */ sad.sin_port = htons((u_short)port); /* Set port */ /* Make listening socket's port reusable - must appear before bind */ option_value = 1; if (setsockopt(sd,SOL_SOCKET, SO_REUSEADDR, (char *)&option_value, sizeof(option_value)) < 0) { perror("setsockopt"); exit(0); } /* Bind a local address to the listening socket */ if (bind(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) { perror("bind failed"); exit(1); } /* Specify size of request queue of the listening socket */ if (listen(sd, 6) < 0) { perror("listen failed"); exit(1); } /* Establish handling of SIGCHLD signal */ if (signal(SIGCHLD, sig_child) == SIG_ERR) { perror("Unable to set up signal handler for SIGCHLD"); exit(1); } /* Main server loop - accept and handle requests */ for (;;) { while (children_count >= MAXCHILD) {pause();} /* Latency would be substantially reduced if we accepted */ /* and closed the connection instead of pausing. But that */ /* would mean more failures. */ alen = sizeof(cad); if ((sd2 = accept(sd, (struct sockaddr *)&cad, &alen)) < 0){ if (errno == EINTR) continue; perror("accept failed"); exit(1); } /* Create child to handle connection */ children_count++; if ((pid = fork()) < 0) { perror("Cannot fork a child"); exit(1); } else if (pid == 0) // in child child(sd2, filename); // The child will not return /* Close the connected socket: it is being handled by child */ close(sd2); } }