/* * CS 290 Sample Code #2 - a clear channel TCP server. * * This server merely copies all input received via the network back to * the network. * * Compilation instructions: * * HPUX: gcc sample2.c -o sample2-hpux -DHAS_HERROR * Solaris: gcc sample2.c -o sample2-solaris -lsocket -lnsl * SunOS: gcc sample2.c -o sample2-sunos -lnsl * FreeBSD: gcc sample2.c -o sample2-fbsd -DHAS_HERROR * Linux: gcc sample2.c -o sample2-linux -DHAS_HERROR * * Solaris and SunOS can't use herror() - they're kind of braindead. * Solaris needs to be linked against both the socket and ns libraries, * SunOS against the NS library, and HPUX is the smartest of them all. * * 02/11/97 - Initial revision - Dave Andersen * */ #include /* HPUX requires for getopt */ #include /* Everyone else requires for getopt :) */ #include /* fprintf, stderr, etc */ #include /* Required for socket types and such */ #include #include /* Internet networking headers */ #include #include #include /* Used for the ioctl stuff */ #include /* (does not relate) */ #include /* * herror() is a very cool function introduced in 4.3BSD Tahoe * - unfortunatly, Sun decided to leave it out. We replace * - it with a less-informative printf for Sun boxes. */ #ifndef HAS_HERROR #define herror(A) printf("%s\n", A) #endif void usage() { printf("syntax: sample2 \n\n"); } void handle_io(int netinout) { char buffer[BUFSIZ]; FILE *them; int pid; fprintf(stderr, "Hey - I've got a connection!\n"); if ((them = fdopen(netinout, "r+")) == NULL) { perror("Could not open file stream on supplied file descriptor"); return; } /* * You'll notice something somewhat ugly in this example - * the use of fflush() before and after the output to the network * file descriptor. This is a shortcoming in the stdio library * (I'm honestly not sure if the fstream library in C++ suffers * from the same limitations - YMMV) which requires that the buffers * be flushed between an input and an output. The second fflush * forces the string to be sent to the user immediately instead * of being buffered. */ while (fgets(buffer, BUFSIZ-1, them) != NULL) { fflush(them); fprintf(them, buffer); fprintf(stderr, "%d: %s", netinout, buffer); fflush(them); } } int do_server(int port) { int newsockfd, sockfd; /* Our socket descriptor */ struct protoent *proto; struct sockaddr_in mysock, theirsock; int childpid, clilen; /* * First, we use getprotobyname to get the protocol number for * what we're trying to do. This isn't amazinglyl necessary - * many implementations just specify 0, the generic IP protocol * number, but we're trying to be anal here. */ if ( ( proto = getprotobyname("tcp")) == NULL) { perror("Could not get protocol number for TCP"); return -1; } if ( (sockfd = socket(AF_INET, /* Internet socket */ SOCK_STREAM, /* Streaming (TCP), not dgram (UDP) */ proto->p_proto)) < 0) { perror("Could not obtain a socket"); return -1; } bzero( (char *)&mysock, sizeof(mysock)); mysock.sin_family = AF_INET; mysock.sin_addr.s_addr = htonl(INADDR_ANY); /* Accept connections on all addresses */ mysock.sin_port = htons(port); if (bind(sockfd, (struct sockaddr *)&mysock, sizeof(mysock)) < 0) { perror("Could not bind local socket\n"); return -1; } listen(sockfd, 5); while (1) { clilen = sizeof(theirsock); newsockfd = accept(sockfd, (struct sockaddr *) &theirsock, &clilen); /* This blocks until we get a connection request. */ fprintf(stderr, "Accept returned sockfd %d\n", newsockfd); if (newsockfd < 0) { perror("Accept error"); return -1; } if ( (childpid = fork()) < 0) { perror("Fork failed"); return -1; } /* Some of this is kind of nasty. We're forking twice here * to avoid dropping zombies: * * After the first fork, we're the main process' child. * We then fork again, after which the first child * exits - this leaves the second child parentless. * because of this, init takes us over, and reaps * us properly when we're ready to exit. * * Sorry for leaving that out of the original. :) */ if (childpid == 0) /* We're the child */ { close(sockfd); if ((childpid = fork()) < 0) { perror("Fork 2 failed\n"); return -1; } else if (childpid > 0) /* We're the first child still */ exit(0); sleep(1); handle_io(newsockfd); fprintf(stderr, "Child with sockfd %d closing\n", newsockfd); exit(0); } close(newsockfd); if (waitpid(childpid, NULL, 0) != childpid) { perror("waitpid error"); return -1; } } } /* A note about handle_io - we're cheating here, because the * point of this sample is the connection establishment code, * not how to handle a select() loop, which you may or may not * need for your assignment. * * Select lets you deal with data as it comes in on file descriptors * and when you can write out to those FDs. * * We're not blocking on reading here, but we're assuming we can always * write - an assumption we should not depend upon. */ /* * A skeleton main - it just calls a few functions and then trusts that * they'll handle everything. */ int main(int argc, char **argv) { struct sockaddr_in sock; int port; int netfd = -1; /* Our network file descriptor - remote end */ if (argc != 2) { usage(); exit(-1); } port = atoi(argv[1]); /* Ordinarily, do more checking here.. */ /* This will segfault if not a number. */ fprintf(stderr, "Opening server on port %d\n", port); do_server(port); exit(0); }