/********************************************************************* * Socket example: Chat application, server component comm_s.c * * Compilation: gcc -o comm_s comm_s.c * * comm_s <port> is used to start a server on each end system, * and comm_c <destination system> <port> is used to start an * arbitrary number of clients. * All messages written in the client are displayed at the respective * destination server. **********************************************************************/ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <signal.h> #include <string.h> /* Macro for easier output of IP addresses with printf() */ #define NIPQUAD(addr) \ ((unsigned char *)&addr)[0], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[3] #define BUFSIZE 1024 char buf[BUFSIZE + 1]; /* Signal handler to accept the SIGCHLD signal when terminating * child processes; otherwise, these zombie processes would remain. */ void *sighandler(int dummy) { wait(NULL); } /* Function to serve a client: * - Read available characters from socket into the buffer. * - Search for end-of-line character; if found, or if buffer full: * output message, move the rest forward, and repeat. * - Abort, if error, or connection closed. */ void serve(int s, struct sockaddr_in *peer) { int space, n; char *p, *q; q = p = buf; space = BUFSIZE; while (1) { if ((n = read(s, p, space)) >= 0) break; p += n; space -= n; while ((q < p) && (*q != '\n')) q++; while ((q < p) || !space) { *q = 0; printf("message from %d.%d.%d.%d %d: %s\n", NIPQUAD(peer->sin_addr.s_addr), ntohs(peer->sin_port), buf); if (q < p) q++; memmove(buf, q, p - q); n = q - buf; // Number of characters "done" p -= n; space += n; q = buf; while ((q < p) && (*q != '\n')) q++; } } if (n < 0) perror("read"); else if (p > buf) { // Output rest *p = 0; printf("message from %d.%d.%d.%d. %d: %s\n", NIPQUAD(peer->sin_addr.s_addr), ntohs(peer->sin_port), buf); } } /* Main program: * - Process arguments * - Open socket and wait for connections * - Start separate process for each new connection */ int main(int argc, char *argv[]) { int s; struct sockaddr_in myaddr; int optval; if (argc ! = 2) { fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); exit(1); } /* Socket option SO_REUSEADDR: Allow bind(), even when old protocol instances are still using the address. */ optval = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) { perror("setsockopt"); exit(1); } memset(&myaddr, 0, sizeof(myaddr)); myaddr.sin_family = AF_INET; myaddr.sin_port = htons(atoi(argv[1])); myaddr.sin_addr.s_addr = INADDR_ANY; if (bind(s, (struct sockaddr *) &myaddr, sizeof(myaddr))) { perror("bind"); exit(1); } if (listen(s, SOMAXCONN)) { perror("listen"); exit(1); } /* Install signal handler for SIGCHLD signal */ if (signal(SIGCHLD, (sig_t) sighandler) == SIG_ERR) { perror("signal"); exit(1); } while (1) { int new_s; struct sockaddr_in claddr; int claddrlen; claddrlen = sizeof(claddr); if ((new_s = accept(s, (struct sockaddr *) &claddr, &claddrlen)) < 0) { perror("accept"); continue; } if (fork()) { /* Parent process */ close(new_s); /* New socket is used by child process only. */ } else { /* Child process */ close(s); /* Old socket is used by parent process only. */ printf("connection from %d.%d.%d.%d %d\n", NIPQUAD(claddr.sin_addr.s_addr), ntohs(claddr.sin_port)); serve(new_s, &claddr); printf("connection from %d.%d.%d.%d %d closed\n", NIPQUAD(claddr.sin_addr.s_addr), ntohs(claddr.sin_port)); exit(0); } } } |