Skip to content

Instantly share code, notes, and snippets.

@PaulaRudy
Last active August 29, 2015 13:56
Show Gist options
  • Select an option

  • Save PaulaRudy/8827892 to your computer and use it in GitHub Desktop.

Select an option

Save PaulaRudy/8827892 to your computer and use it in GitHub Desktop.
A simple TCP socket server
/*
============================================================================
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