/* multiserver1.c - Simple TCP server that forks MAXCHILD children. * Then it waits for a child to terminate accidentaly, in which case a new * child is forked to take its place. * * All children block on an accept statement on the shared * listening socket. When a child successfully accepts a connection, it * redirects standard input and output to the connection. * When a request arrives a child will read the request, treat it as * a nonsense string, and reply by writing to the client the content * of the file index.html. Then it will close the connection and go back * to the accept statement. * * WARNING: in some other Unix systems (I am using Digital Unix) apparently * when there is a connection all children complete the accept statement, * but only one with success. All others have to go back to the accept statement. * * The main program of the server is created with the command * % gcc -o multiserver1 multiserver1.c * and is invoked with the command * % multiserver1 [port] * where * port:protocol port number to use * The port argument is optional. If no protocol port is * specified, the server uses the default given by PROTOPORT (5195). * * You may see the connections that exist at a particular time on the server * with the command * % netstat | grep 5195 * where 5195, or whatever, is the port used by the server. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int errno; char *buffer; /* buffer where the file is read into */ int bufsize; /* size of buffer = file size + 1 */ /* Executed by each child - Read request, and write the buffer */ void child_main(void) { enum {SEGSIZE = 8192}; int n; char readbuf[SEGSIZE]; int writeseg = (bufsize < SEGSIZE)?bufsize:SEGSIZE; char *cursor; for ( ; ; ) { n = read(0, readbuf, SEGSIZE); if (n <= 0) { /* We check to make sure that read did not fail because of an intervening signal */ if (errno == EINTR) { fprintf(stderr, "Signal while the child was reading\n"); continue; } break; } } close(0); /* Close read end */ for (cursor = buffer; cursor < buffer + writeseg; ) { n = write(1, cursor, writeseg); if (n <= 0) { if (errno == EINTR) { fprintf(stderr, "Signal while writing to the child\n"); continue; } } else cursor += n; } close(1); /* Close write end */ return; } /* Code executed by the child process: it uses the listening socket sd to accept a client's request on a connected socket. After redirecting the socket to standard input and output, calls the child_main routine, and `then is ready for a new connection. */ void child (int sd) { int sd2; /* connected socket's descriptor */ struct sockaddr_in cad; /* structure to hold client's address*/ int alen; /* length of address */ for ( ; ; ) { alen = sizeof(cad); if ((sd2 = accept(sd, (struct sockaddr *)&cad, &alen)) < 0) continue; /* Redirect standard input and standard output to this socket and close the socket */ if ((dup2(sd2, STDIN_FILENO) != STDIN_FILENO) || (dup2(sd2, STDOUT_FILENO) != STDOUT_FILENO)) exit(0); close(sd2); child_main(); } } void filetobuffer(const char *filename) { int fid; /* descriptor for file to be sent to client */ struct stat filestat; /* stats for fid */ if ((fid = open(filename, O_RDONLY)) == -1) { fprintf(stderr, "Unable to open %s\n", filename); exit(1); } if (fstat(fid, &filestat) == -1) { fprintf(stderr, "Unable to stat %s\n", filename); exit(1); } bufsize = filestat.st_size; buffer = (char *)malloc((size_t)(1+bufsize)); if (buffer == 0) { perror("Out of memory"); exit(1); } if (read(fid, buffer, bufsize) != bufsize) { fprintf(stderr, "Cannot read all of %s\n", filename); exit(1); } printf ("bufsize = %d\n", bufsize); } /* simple routine that creates a child and passes to it the socket sd*/ pid_t create_process(int sd) { pid_t pid; if ((pid = fork()) < 0) { perror("Cannot fork a child"); exit(1); } else if (pid == 0) /* in child */ child(sd); /* The child will not return */ return pid; } /*****************************************************************/ #define PROTOPORT 5195 /* default protocol port number*/ #define MAXCHILD 10 /* maximum number of concurrent children */ int main(int argc, char *argv[]) { struct sockaddr_in sad;/* structure to hold server's address*/ int sd; /* listening socket descriptor */ int port; /* protocol port number */ pid_t pid; /* process id of child process */ int children_count; /* number of children being forked */ int option_value; /* needed for setsockopt */ /* Check command-line argument. Use default if necessary. */ port = (argc > 1)?atoi(argv[1]):PROTOPORT; /* Read file to buffer */ filetobuffer("index.html"); /* Create listening socket */ if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(1); } /* 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); } /* 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 */ /* 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); } /* create the children processes */ for (children_count = 0; children_count < MAXCHILD; ++children_count) pid = create_process(sd); /* If a child terminates, create a new child */ for (;;) { pid = wait(0); printf("Process %d terminated\n", pid); pid = create_process(sd); } }