Table of Contents

Project: Time Server

A project for Systems Programming by Brad Hammond during the Fall of 2011.

Objectives

To get a better understanding of socket programming on a Unix System.

Prerequisites

In order to successfully accomplish/perform this project, the listed resources/experiences need to be consulted/achieved:

Background

The idea here is get a little better understanding of socket programming on a lower level. In Java it's fairly simple and to the point. In C, there's a lot more low level manipulation to be done. One of the things I think the book should spend a little more time on is explaining what the structs here are and what the members are used for. In Joe's HPC Systems and Networking class I had come across a C Unix sockets tutorial that I found my way back to. I think it does a decent job at breaking down what's going on in the code.

Attributes

Code

Server

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#include <string.h>
 
#define PORT 13000
#define HOST_LEN 256
#define error(errMsg) {perror(errMsg); exit(1);}
 
int main(int argc, char** argv) 
{
	struct sockaddr_in address;				//Stores internet address info 
	struct hostent *hostEntries;			//Stores info about the host name, name length
	char name[HOST_LEN];					//Name of the server
	int sockID, sockFD, visitorCount = 0;	//ID & file descriptor for the socket, current visitor number
	FILE *sockFP;							//Used to write to the socket
	time_t currTime;						//Current time
 
	//Get a socket
	if ((sockID = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		error("Cannot get socket");
 
	//Zero the address structure
	bzero((void*) &address, sizeof(address));
	//Get host name and fill hostEntries struct
	gethostname(name, HOST_LEN);
	hostEntries = gethostbyname(name);
	//Copy the h_addr member to the sin_addr member
	bcopy((void*) hostEntries -> h_addr, (void*) &address.sin_addr, hostEntries -> h_length);
	//Convert port in host byte order to network byte order
	address.sin_port = htons(PORT);
	//Set .sin_family to AF_INET, internet address
	address.sin_family = AF_INET;
 
	//Bind to the socket
	if (bind(sockID, (struct sockaddr*) &address, sizeof(address)) != 0)
		error("Cannot bind to socket");
 
	//Listen for incomming connections
	if (listen(sockID, 1) != 0)
		error("Cannot listen on socket");
 
	//accepts an incomming connection. Set the FP to the socket
	//so we can write to it. Once the connection is established
	//we can send messages to the user. What visitor number they 
	//are, the time, etc. Then we close the fp.
	while (1) {
		if ((sockFD = accept(sockID, NULL, NULL)) == -1)
			error("Cannot accept on socket");
		if ((sockFP = fdopen(sockFD, "w")) == NULL)
			error("Cannot open file pointer");
 
		currTime = time(NULL);
		fprintf(sockFP, "Welcome to %s! ", name);
		fprintf(sockFP, "The current time is %s", ctime(&currTime));
		fprintf(sockFP, "You are visitor #%d\n", ++visitorCount);
		fclose(sockFP);
	}
 
	return 0;
}

Client

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
 
#define error(errMsg) {perror(errMsg); exit(1);}
#define STDOUT 1
 
int main(int argc, char **argv)
{
	struct sockaddr_in servAddress; 	//Address of the server to connect
										//to
	struct hostent *hostEntries;		//Stores the name of the server and the
										//server name's length
	int sockID, sockFD;					//socket ID and file descriptor
	char *message;						//Message to get from server
	int messageLen;						//length of the message;
 
	//Check arguments. Needs a hostname and port number
	if (argc != 3) 
		error("no arguments");
 
	//Malloc space for the message and zero the socket address struct
	message = (char*) malloc(sizeof(char) * BUFSIZ);
	bzero(&servAddress, sizeof(servAddress));
 
	//Open the socket
	if ((sockID = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		error("Cannot open socket.");
 
	//Get the hostname and store it in the hostEntries struct
	if ((hostEntries = gethostbyname(argv[1])) == NULL)
		error(argv[1]);
 
	//Copy h_addr member to sin_addr member, store the port, and store the socket type 
	//IPV4 socket.
	bcopy(hostEntries->h_addr, (struct sockaddr*)&servAddress.sin_addr, hostEntries->h_length);
	servAddress.sin_port = htons(atoi(argv[2]));
	servAddress.sin_family = AF_INET;
 
	//Connect to the socket
	if (connect(sockID, (struct sockaddr*)&servAddress, sizeof(servAddress)) != 0)
		error("Cannot conect to socket.");
 
	//Read the message in from the socket
	if ((messageLen = read(sockID, message, BUFSIZ)) == -1)
		error("Cannot read from socket");
 
	//Write to stdout
	if (write(STDOUT, message, messageLen) != messageLen)
		error("Cannot write to stdout");
 
	//Close the file descriptor and free memory from message	
	close(sockID);
	free(message);
 
	return 0;
}

Execution

Again, if there is associated code with the project, and you haven't already indicated how to run it, provide a sample run of your code:

brad@Lucid-Lynx:/media/06F6-9DC2/Notepad++/Programs$ ./clientThing `hostname` 13000
Welcome to Lucid-Lynx! The current time is Thu Dec 15 12:16:41 2011
You are visitor #1
brad@Lucid-Lynx:/media/06F6-9DC2/Notepad++/Programs$ ./clientThing `hostname` 13000
Welcome to Lucid-Lynx! The current time is Thu Dec 15 12:16:43 2011
You are visitor #2
brad@Lucid-Lynx:/media/06F6-9DC2/Notepad++/Programs$ ./clientThing `hostname` 13000
Welcome to Lucid-Lynx! The current time is Thu Dec 15 12:16:44 2011
You are visitor #3
brad@Lucid-Lynx:/media/06F6-9DC2/Notepad++/Programs$ 

Reflection

I feel a little bit better about the structs used in socket programming. It was a bit overwhelming at first having to know all of the members you use and how to use them. I'm still a bit fuzzy on some of it but I definitely have a better understanding than I did before doing this.

I think I'm pretty good on this stuff now. It's just a case of trying to remember what parameters each function will take. Some of the syntax is a bit crazy with the struct pointers being casted and such.

References

In performing this project, the following resources were referenced:

Generally, state where you got informative and useful information to help you accomplish this project when you originally worked on it (from Google, other wiki documents on the Lab46 wiki, etc.)