/** * A simple preforking echo server in C. * * Building: * * $ gcc -Wall -o echo echo.c * * Usage: * * $ ./echo * * ~ then in another terminal ... ~ * * $ echo 'Hello, world!' | nc localhost 4242 * * Inspiration: * http://tomayko.com/writings/unicorn-is-unix * http://jacobian.org/writing/python-is-unix/ */ #include /* fork, close */ #include /* exit */ #include /* strlen */ #include /* perror, fdopen, fgets */ #include #include /* waitpid */ #include /* getaddrinfo */ #define die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) #define PORT "4242" #define NUM_CHILDREN 3 #define MAXLEN 1024 int readline(int fd, char *buf, int maxlen); // forward declaration int main(int argc, char** argv) { int i, n, sockfd, clientfd; int yes = 1; // used in setsockopt(2) struct addrinfo *ai; struct sockaddr_in *client; socklen_t client_t; pid_t cpid; // child pid char line[MAXLEN]; char cpid_s[32]; char welcome[32]; /* Create a socket and get its file descriptor -- socket(2) */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { die("Couldn't create a socket"); } /* Prevents those dreaded "Address already in use" errors */ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&yes, sizeof(int)) == -1) { die("Couldn't setsockopt"); } /* Fill the address info struct (host + port) -- getaddrinfo(3) */ if (getaddrinfo(NULL, PORT, NULL, &ai) != 0) { die("Couldn't get address"); } /* Assign address to this socket's fd */ if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) != 0) { die("Couldn't bind socket to address"); } /* Free the memory used by our address info struct */ freeaddrinfo(ai); /* Mark this socket as able to accept incoming connections */ if (listen(sockfd, 10) == -1) { die("Couldn't make socket listen"); } /* Fork you some child processes. */ for (i = 0; i < NUM_CHILDREN; i++) { cpid = fork(); if (cpid == -1) { die("Couldn't fork"); } if (cpid == 0) { // We're in the child ... for (;;) { // Run forever ... /* Necessary initialization for accept(2) */ client_t = sizeof client; /* Blocks! */ clientfd = accept(sockfd, (struct sockaddr *)&client, &client_t); if (clientfd == -1) { die("Couldn't accept a connection"); } /* Send a welcome message/prompt */ bzero(cpid_s, 32); bzero(welcome, 32); sprintf(cpid_s, "%d", getpid()); sprintf(welcome, "Child %s echo> ", cpid_s); send(clientfd, welcome, strlen(welcome), 0); /* Read a line from the client socket ... */ n = readline(clientfd, line, MAXLEN); if (n == -1) { die("Couldn't read line from connection"); } /* ... and echo it back */ send(clientfd, line, n, 0); /* Clean up the client socket */ close(clientfd); } } } /* Sit back and wait for all child processes to exit */ while (waitpid(-1, NULL, 0) > 0); /* Close up our socket */ close(sockfd); return 0; } /** * Simple utility function that reads a line from a file descriptor fd, * up to maxlen bytes -- ripped from Unix Network Programming, Stevens. */ int readline(int fd, char *buf, int maxlen) { int n, rc; char c; for (n = 1; n < maxlen; n++) { if ((rc = read(fd, &c, 1)) == 1) { *buf++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return 0; // EOF, no data read else break; // EOF, read some data } else return -1; // error } *buf = '\0'; // null-terminate return n; }