/* File: chat.c * Author: Samuel A. Rebelsky * Version: 1.1 of February 2000 * * A very simple semi-synchronous chat application. One * person acts as "server" and the other as "client". The * server starts up and then the client. The client gets * to write first, and transfers control by writing "o" * on a line by itself. The server then gets to write and, * again, transfers control with "o". This continues until * one side or the other writes "oo". * * Note that this application checks the name of the command * used to run it. If the command name is "cserver", it assumes * that it's a server. If the command name is "cclient", it * assume that it's a client. * * This application has one potential bug: If lines of text * are too long and end in "oo" or "o", they may be interpreted * as transfering control or exiting the application. * * The code for this application is borrowed and heavily * modified from pp. 45-48 of "Computer Networks: A Systems * Approach", 2nd Edition, by Larry L. Peterson and Bruce S. Davie. * * Compilation (Unix): * $(CC) -c chat.c * $(CC) -o chat chat.o * ln -s chat cserver * ln -s chat cclient * Usage: * server * client server host * * Exit values: * 0: Success * 1: Incorrect number of parameters * 2: Unknown host * 3: Trouble creating a socket * 4: Trouble connecting or accepting connection * 5: Invalid program name * 6: Trouble binding socket */ /************* * Libraries * **********************************************************/ #include #include #include #include #include #include #include /************* * Constants * **********************************************************/ #define SERVER_PORT 5432 #define MAX_LINE 256 #define MAX_PENDING 5 /**************************** * Function Predeclarations * **********************************************************/ /* Receive messages. Returns 1 if anything other than * oo received. Returns 0 if it receives oo. * Pre: s is an active socket. */ int receive(int s); /* Send messages. Returns 1 if the last thing typed is * o. Returns 0 if the last thing typed is oo. * Pre: s is an active socket. */ int spew(int s); /* Set up the "server" and "client". Returns a socket you * can use for communication. Either may exit if an error * occurs. * Post: Returns an active socket */ int setupServer(); int setupClient(char *hostName); /******** * Main * **********************************************************/ int main(int argc, char *argv[]) { /* I. Declare variables. */ int s; /* The socket used for communication. */ /* II. Decide what role to play. */ /* A. It's the server. */ if (strcmp(argv[0],"cserver") == 0) { /* 1. Set up the server. */ s = setupServer(); /* 2. Alternately receive and send messages. */ while (receive(s) && spew(s)) ; } /* B. It's the client. */ else if (strcmp(argv[0], "cclient") == 0) { /* 1. Set it up. */ if (argc == 2) { s = setupClient(argv[1]); } else { fprintf(stderr, "Usage: %s servername\n", argv[0]); exit(1); } /* 2. Alternately send and receive messages. */ while (spew(s) && receive(s)) ; } /* C. It's neither. Crash. */ else { fprintf(stderr, "Sorry, this can only be run as cserver or cclient.\n"); exit(5); } /* III. Clean up. */ close(s); exit(0); } // main() /*********** * Helpers * **********************************************************/ int receive(int s) { /* An array of MAX_LINE characters. Used for one line of input. */ char buf[MAX_LINE]; /* The length of a message that was just read. */ int len; /* I. Tell the user what's up. */ printf("\n********* RECEIVING ************\n"); /* II. Repeatedly receive lines. */ while ((len = recv(s, buf, sizeof(buf), 0)) != 0) { /* Print the message. */ printf("* "); fputs(buf, stdout); /* See if we're switching control */ if (strcmp(buf, "o\n") == 0) return 1; else if (strcmp(buf, "oo\n") == 0) return 0; } // while /* III. If we've fallen through the cracks, something * weird happened. Live with it. */ fprintf(stderr, "Connection terminated abruptly.\n"); return 0; } /* receive */ int spew(int s) { /* An array of MAX_LINE characters. Used for one line of input. */ char buf[MAX_LINE]; /* The length of a message that was just read. */ int len; /* I. Prompt the user. */ printf("\n********** SENDING *************\n"); printf("Enter lines of text to send. Enter 'o' on a line\n"); printf("to transfer control. Enter 'oo' on a line to quit.\n"); printf("> "); /* II. Main loop: Get and send lines of text. */ while (fgets(buf, sizeof(buf), stdin)) { /* This sets the last character to the null character. * I'm not sure why this is necessary. */ buf[MAX_LINE-1] = '\0'; /* Since the string may have ended early (e.g., with a carriage * return), we get the length. */ len = strlen(buf) + 1; /* And we send everything. */ send(s, buf, len, 0); /* Did the user switch control? */ if (strcmp(buf, "o\n") == 0) return 1; else if (strcmp(buf, "oo\n") == 0) return 0; /* Prompt again. */ printf("> "); } /* while */ /* II. If we got to this point, the user hit ^D instead of oo. Recover. */ send(s, "oo\n", 4, 0); return 0; } /* spew */ int setupClient(char *host) { /* I. Declare Variables */ /* A struct is a group of fields (record). This particular one has information on the current host. */ struct hostent *hp; /* The address of something. */ struct sockaddr_in sin; /* The number of the socket we use. */ int s; /* III. Get information on the server. */ /* Translate host name into IP address. */ hp = gethostbyname(host); /* Hack! Many C routines, including gethostbyname, return the * null (0) pointer when they fail. In C, 0 is "false". */ if (hp == NULL) { fprintf(stderr, "unknown host: %s\n", host); exit(2); } /* IV. Set up the address. */ bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); sin.sin_port = htons(SERVER_PORT); /* V. Open the socket. */ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("failed to create socket"); exit(3); } if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("failed to connect"); close(s); exit(4); } /* VI. We succeded, so return the socket number. */ return s; } /* setupClient() */ int setupServer() { /* I. Declare variables. */ struct sockaddr_in sin; int s; int new_s; int addrlen; /* II. Build address data structure. */ bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(SERVER_PORT); /* III. Set up passive open. */ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("server: trouble creating socket."); exit(3); } if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0) { perror("server: trouble binding socket"); exit(6); } listen(s, MAX_PENDING); /* IV. Wait for connections. */ printf("Waiting for client to connect.\n"); if ((new_s = accept(s, (struct sockaddr *)&sin, &addrlen)) < 0) { perror("server: trouble accepting connection"); exit(4); } /* V. Wo hoo! We have a connection. Drop the original socket * and return this new one. */ close(s); return new_s; } /* setupServer() */