Last active
August 29, 2015 13:56
-
-
Save PaulaRudy/8827892 to your computer and use it in GitHub Desktop.
A simple TCP socket server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ============================================================================ | |
| Name : server.c | |
| Author : Paula Rudy (paular@wpi.edu) | |
| Version : 0 | |
| Description : A simple TCP stream socket server that chooses a script randomly | |
| from 3 possibilities. | |
| Regardless of script, server sends a text greeting to the client, | |
| receives a reply, and sends a response. | |
| Citations : Basic layout of the program and some code was sourced from | |
| sample code provided at: | |
| http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html | |
| Thanks to Brian "Beej Jorgensen" Hall (beej@beej.us) for this | |
| wonderful resource. | |
| ============================================================================ | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <errno.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <netdb.h> | |
| #include <arpa/inet.h> | |
| #include <sys/wait.h> | |
| #include <signal.h> | |
| #include <time.h> | |
| #define PORT "9191"//The port incoming connections will be connecting to | |
| #define MAXWAITINGCLIENTS 10//How many pending connections (clients) the queue will hold | |
| #define MAXDATASIZE 100 //The max number of bytes we can receive at once | |
| //Handler used to reap dead processes | |
| void sigchld_handler(int s) | |
| { | |
| while(waitpid(-1, NULL, WNOHANG) > 0); | |
| } | |
| //Grab the address stored in the sockaddr pointed to by sa, IPv4 *or* IPv6: | |
| void *getAddrFromSockaddr(struct sockaddr *sa) | |
| { | |
| //If the sockaddr is IPv4... | |
| if (sa->sa_family == AF_INET) | |
| return &(((struct sockaddr_in*)sa)->sin_addr);//return the IP address stored in the sin_addr of sa | |
| //The sockaddr must be IPv6 | |
| return &(((struct sockaddr_in6*)sa)->sin6_addr);//return the IP address stored in the sin6_addr of sa | |
| } | |
| int main(void) | |
| { | |
| int frontDoor, newDoor;//Server listens for incoming connections on frontDoor, and receives/sends information via a new connection using "newDoor" | |
| struct addrinfo prepInfo;//Used to store information on the criteria for selecting the socket address structures returned in the listOfServerAddr. | |
| struct addrinfo *listOfServerSocketAddr;//The list of appropriate socket address structures available | |
| struct addrinfo *iteratorForListOfServerSocketAddr;//Used to iterate through listOfServerSocketAddr | |
| struct sockaddr_storage clientAddress;//Client's address information | |
| socklen_t sin_size;//Used for accepting new connections | |
| struct sigaction sa;//Used to reap dead processes | |
| int yes=1;//Used to set socket options | |
| char clientAddressString[INET6_ADDRSTRLEN];//Used to store a calling client's IP address as a string | |
| int returnValueFromgetaddrinfo;//Used to store a return value from a call to getaddrinfo | |
| int messageType;//This will we later initialized to a random number from 0 to 2 (inclusive) and used to determine which greeting we send (and so what "script" we use) | |
| int numBytesRecieved;//Used to store the number of bytes received from the client (will be at most MAXDATASIZE-1) | |
| char buffer[MAXDATASIZE];//This will hold the string received through the connection (the client's reply to our greeting) | |
| //Set up prepInfo to say what kind of socket we want (prepInfo specifies the criteria for selecting the socket address structures returned in the listOfServerAddr) | |
| memset(&prepInfo, 0, sizeof prepInfo); | |
| prepInfo.ai_family = AF_UNSPEC; // *either* IPv4 or IPv6 | |
| prepInfo.ai_socktype = SOCK_STREAM;//only use TCP | |
| prepInfo.ai_flags = AI_PASSIVE; //use my IP a | |
| //Set up the list of appropriate socket address structures available | |
| if ((returnValueFromgetaddrinfo = getaddrinfo(NULL, PORT, &prepInfo, &listOfServerSocketAddr)) != 0) {//If it fails... | |
| fprintf(stderr, "server: getaddrinfo: %s\n", gai_strerror(returnValueFromgetaddrinfo));//error setting up listOfServerSocketAddr | |
| return 1; | |
| } | |
| //Loop through all the results in listOfServerSocketAddr, create a socket and bind to the first address we can | |
| for(iteratorForListOfServerSocketAddr = listOfServerSocketAddr; iteratorForListOfServerSocketAddr != NULL; iteratorForListOfServerSocketAddr = iteratorForListOfServerSocketAddr->ai_next) { | |
| //Try to create the socket | |
| if ((frontDoor = socket(iteratorForListOfServerSocketAddr->ai_family, iteratorForListOfServerSocketAddr->ai_socktype, | |
| iteratorForListOfServerSocketAddr->ai_protocol)) == -1) { //If it fails... | |
| perror("server: socket()");//print error to error stream | |
| continue; | |
| } | |
| //Try to set the socket options | |
| if (setsockopt(frontDoor, SOL_SOCKET, SO_REUSEADDR, &yes, | |
| sizeof(int)) == -1) {//If it fails... | |
| perror("server: setsockopt()"); | |
| exit(1); | |
| } | |
| //Try to bind the socket (assign the address contained in the addrinfo structure pointed to by iteratorForListOfServerSocketAddr to the socket) | |
| if (bind(frontDoor, iteratorForListOfServerSocketAddr->ai_addr, iteratorForListOfServerSocketAddr->ai_addrlen) == -1) {//If it fails... | |
| close(frontDoor);//close the socket | |
| perror("server: bind()"); | |
| continue; | |
| } | |
| //Socket successfully bound | |
| break; | |
| } | |
| if (iteratorForListOfServerSocketAddr == NULL) {//If we have iterated over the list of addrinfo ("listOfServerSocketAddr") and failed to successfully bind a socket to any of them... | |
| fprintf(stderr, "server: failed to bind\n"); | |
| return 2; | |
| } | |
| freeaddrinfo(listOfServerSocketAddr);//We're all done with this structure, so free the memory | |
| //Try to listen on "frontdoor" for incoming connection requests. These requests are queued in a queue of max length "MAXWAITINGCLIENTS" | |
| if (listen(frontDoor, MAXWAITINGCLIENTS) == -1) {//If it fails... | |
| perror("server: listen()"); | |
| close(frontDoor); | |
| exit(1); | |
| } | |
| // Try to reap all dead processes | |
| sa.sa_handler = sigchld_handler; | |
| sigemptyset(&sa.sa_mask); | |
| sa.sa_flags = SA_RESTART; | |
| if (sigaction(SIGCHLD, &sa, NULL) == -1) {//If it fails... | |
| perror("server: sigaction()"); | |
| close(frontDoor); | |
| exit(1); | |
| } | |
| //Print a message to the console indicating a successful set up | |
| printf("Server is now waiting for connections...\n \n"); | |
| // Main loop used to accept incoming connections | |
| while(1) { | |
| //Open a new socket ("newDoor") for the first connection request on the listen queue | |
| sin_size = sizeof clientAddress; | |
| newDoor = accept(frontDoor, (struct sockaddr *)&clientAddress, &sin_size); | |
| if (newDoor == -1) {//If failure accept()ing: | |
| perror("server: accept()"); | |
| close(frontDoor); | |
| close(newDoor); | |
| exit(1); | |
| } | |
| //Find and store the calling client's IP address to a string | |
| inet_ntop(clientAddress.ss_family, | |
| getAddrFromSockaddr((struct sockaddr *)&clientAddress), | |
| clientAddressString, sizeof clientAddressString); | |
| //Print a message to the console to indicate a connection has been received | |
| printf("Server: got connection from %s\n", clientAddressString); | |
| //Fork off a new process to handle the newly accepted connection | |
| if (!fork()) { | |
| close(frontDoor); //The child process doesn't need the listener | |
| srand(time(NULL));//Initialize the rand() function with a seed from the current time | |
| messageType = rand() % 3;//Choose a random number from 0 to 2 (inclusive). This will determine which greeting we send (and so what "script" we use) | |
| switch (messageType) | |
| { | |
| case 0: | |
| //Send script 0's greeting: | |
| if (send(newDoor, "Who goes there?", 15, 0) == -1) { //If it fails... | |
| perror("server: send greeting script 0"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful greeting | |
| printf("Server: sent \"Who goes there?\" \n"); | |
| //Try to pull no more than MAXDATASIZE-1 bytes through the socket into the buffer to grab the client's reply | |
| if ((numBytesRecieved = recv(newDoor, buffer, MAXDATASIZE-1, 0)) == -1) {//If it fails... | |
| perror("server: receive reply script 0"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Finish the reply string | |
| buffer[numBytesRecieved] = '\0'; | |
| //Print the string received into the console | |
| printf("Server: received \"%s\"\n",buffer); | |
| //Send script 0's reply | |
| if (send(newDoor, "Uh, hi Ishmael.", 15, 0) == -1) {//If it fails... | |
| perror("server: send reply script 0"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful reply | |
| printf("Server: sent \"Uh, hi Ishmael.\" \n \n"); | |
| break; | |
| case 1: | |
| //Send script 1's greeting: | |
| if (send(newDoor, "Greetings! How can I help you?", 30, 0) == -1) {//If it fails... | |
| perror("server: send greeting script 1"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful greeting | |
| printf("Server: sent \"Greetings! How can I help you?\" \n"); | |
| //Try to pull no more than MAXDATASIZE-1 bytes through the socket into the buffer to grab the client's reply | |
| if ((numBytesRecieved = recv(newDoor, buffer, MAXDATASIZE-1, 0)) == -1) {//If it fails... | |
| perror("server: receive reply script 1"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Finish the string | |
| buffer[numBytesRecieved] = '\0'; | |
| //Print the string received into the console | |
| printf("Server: received \"%s\"\n",buffer); | |
| //Send script 1's reply | |
| if (send(newDoor, "I'm a server, not a timepiece, Jim!", 35, 0) == -1) {//If it fails... | |
| perror("server: send reply script 1"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful reply | |
| printf("Server: sent \"I'm a server, not a timepiece, Jim!\" \n \n"); | |
| break; | |
| case 2: | |
| //Send script 2's greeting: | |
| if (send(newDoor, "What is your quest?", 19, 0) == -1) {//If it fails... | |
| perror("server: send greeting script 2"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful greeting | |
| printf("Server: sent \"What is your quest?\" \n"); | |
| //Try to pull no more than MAXDATASIZE-1 bytes through the socket into the buffer to grab the client's reply | |
| if ((numBytesRecieved = recv(newDoor, buffer, MAXDATASIZE-1, 0)) == -1) {//If it fails... | |
| perror("server: receive reply script 2"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Finish the string | |
| buffer[numBytesRecieved] = '\0'; | |
| //Print the string received into the console | |
| printf("Server: received \"%s\"\n",buffer); | |
| //Send script 2's reply | |
| if (send(newDoor, "Right. Off you go.", 18, 0) == -1) { | |
| perror("server: send reply script 2"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| } | |
| //Indicate a successful reply | |
| printf("Server: sent \"Right. Off you go.\" \n \n"); | |
| break; | |
| default: | |
| //This will be reached if messageType is not 0, 1, or 2. This should never happen, so we print an error, close the socket, and exit. | |
| perror("server: messageType not recognized"); | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(1); | |
| break; | |
| } | |
| close(newDoor);//This child process is done, so we don't need this anymore | |
| exit(0);//Close this child process | |
| } | |
| close(newDoor);//We're all done, so the parent process doesn't need this anymore | |
| } | |
| close(frontDoor);//We're all done, so the parent process doesn't need this anymore | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment