String Mangler Client/Server Program

Due: Sunday, Feb. 5 before 11:30pm

This assignment will give you some practice writing a client-server application using TCP sockets. You should write your solution in C or C++ that calls Unix socket system calls.

You must work with one partner on this assignment, and I'd like at least one partner in each group to be someone who has taken CS45.



Project description
Running your socket program
Tips for getting started and C Style Guide including some useful system calls
What to Submit and Demo

Project Description

For this assignment, you will write a string mangling server (and client). The client and server should use TCP sockets to communicate. A client will connect to the server, and send it a message containing the string to mangle. The server will mangle the string in some well-defined way, and return the result to the client. The client continues sending string until either the client quits and closes the connection. You should structure your client so that it reads in a string to mangle from the user, and continues to do so until the user chooses to quit.

The server should be able accept multiple, simultaneous client connections so that clients requests do not interfere with each other. However, when you first write the server, write it so that it supports just one client connection at a time, then add support for multiple, simultaneous connections once that works.

The server program should never exit (except when you CNTL-C it, it should exit); if no clients are currently connected, it just waits around for the next client connection. Your server should detect when a client has exited and terminate its end of the connection.

You can decide how your server mangles strings, but your server should be consistent (i.e. is should be either a reverse mangler or a cyclic cipher mangler, or ...). Here are some examples of ways to mangle a string:

I'd like each group to do something slightly different with their server, so let other groups know how you are mangling strings.

Running your client and server

To run, you will start the server on one lab machine, lets say curry, and start the client on another lab machine (just use ssh to remotely log into the other machine). The client needs to be started with the server's IP as a command line argument. To get the server's IP, use nslookup:
%  nslookup curry
  ...
  Address  130.58.68.36

% client 130.58.68.36      # start the client and try to connect to server
			    # running on curry, 

# client program should print a prompt for user to enter text
Enter the next message:  hello there

# then send the "hello there" string to the server that will mangle the
# received string and send the mangled string back to the client.  As a 
# debugging step, have the server print out the received string and 
# the returned mangled string. 

# the client should then print out the result
HELLO THERE

#the client repeats printing the prompt, reading input, sending ...
Do you want to continue (yes/no): yes
Enter the next message: 

Getting Started

Start by copying over my starting point code into your private directory:

% cp /home/newhall/public/cs85/hw1_startingpt/* .
% ls
Makefile  README  client.c  mangler.h  server.c
Read through the README file and the comments in the client and server to help you get going. Also, I'd suggest looking over a TCP/IP programming reference before you get going:

Read through my C Style Guide and make sure that your code conforms to it (good comments, good modular design, robust, etc.).

Start by getting the TCP connection protocol correct: implement a simple client and server program that create sockets and connect and pass a simple message from client to server (send an int value for example), and then try sending a simple response message from server to client.

Once that works, implement the mangling server so that it handles just a single client connection at a time. Once you get that to work, then add support to the server for handling multiple, simultaneous client connetions, and making sure to clean up dead child server processes when they exit (get rid of zombies). Finally, stress test your solution to ensure it is both correct and robust.

Some problems you will need to think about: how the server knows when it has read all of the string sent to it by a client in a single request; how the server knows when a client has terminated its connection; and how to ensure that two clients requests don't interfere with each other.

help with specific detais of project:

supporting multiple client connections

To do this you will need to fork off a new thread to handle the connection of each client. The fork system call creates a new (child) process, and both parent and child continue from after the fork call. fork returns 0 to the child and the pid of the child to the parent, so you can have child and parent processes follow different paths through the code based on fork's return value:
int  pid;

// create a new child process
pid = fork();

if(pid == -1) { 
   // fork returned an error, handle it

} else if (pid == 0) {   // this is the child process:
   // child code to handle this specific connection
   // until the child exits

} else { // this is the parent process:
   // parent process to get ready to accept the next connection
  
}

cleaning up server threads after client's exit

Your server should fork a server thread to handle each client connection, and then clean up that server thread when the connection is closed. To do this, you need to have the parent process (the main server process) catch and handle a SIGCHILD signal from each exiting server thread (otherwise you will have zombie server child processes).

In the server set up a sighandler routine that will be invoked when the parent receives a SIGCHILD from an exiting child process:

void my_sigchild_handler(int x)
{
        while(waitpid(-1, NULL, WNOHANG) > 0) {  }
}
Before accepting connections and forking child threads, register your signal handler on SIGCHILD by calling sigaction:
struct sigaction myhandler;

...

//set the handler to my_sigchild_handler
myhandler.sa_handler = my_sigchild_handler; 
// initialize the set of signals:
sigemptyset(&myhandler.sa_mask);
// set the flags...I'm not sure what the best setting is, 
//   but this works pretty well (it is a flag that say to 
//   restart a system call if the signal occurred during a syscall)
myhandler.sa_flags = SA_RESTART;
// call sigaction to register my handler with SIGCHILD signals:
if (sigaction(SIGCHLD, &myhandler, NULL) == -1) {
    // something bad happend, lets exit
    perror("sigaction");
    exit(1);
}

See the man pages for sigaction and wait to get include files and to find out more about sigaction.

setting sockopts

set sockopts: SO_REUSEADDR & SO_LINGER If you set the sockoption SO_LINGER to off, then the socket will close immediately upon a process exit:
	struct linger linger_val;

	linger_val.l_onoff = 0;
	linger_val.l_linger = 0;

	setsockopt(sock_fd, , SO_LINGER, (void *)linger_val, 
		(socklen_t) (sizeof(struct linger));

getting socket and IP info

# info for tcp sockets on  machine	

netstat -tcp -v		

# proc files with socket info:

/proc/net/dev           # device information
/proc/net/raw           # raw socket information
/proc/net/tcp           # TCP socket information
/proc/net/udp           # UDP socket information
/proc/net/ipx           # IPX socket information
/proc/net/snmp           # statistics

# get my IP addr

ifconfig                # in the eth0 entry it is "inet addr:" entry

getting the state of a process

# one way to find state of server processes: ps -A | grep myserver 27318 pts/2 00:00:00 myserver 27321 pts/2 00:00:00 myserver # if you see a listing like this, then you are not correctly handling SIGCHILDS 27321 pts/2 00:00:00 myserver <defunct> # /proc/pid contains info about process with matching pid: cat /proc/27321/status # this contains the state of the process

What to Handin

Create a tar file of the directory containing:
  1. a README file with (1) both your names (2) information about how to run your programs (3) anything else you want to tell me about your solution
  2. a Makefile for building your client and server programs
  3. all the source and header files I need to build your programs:
    (I should only have to type 'make' to build them)
Email me your tar file as an attachment sometime before the due date. You may want to cc yourself to make sure that what you sent me is your solution.

see my Unix Help pages for information about creating a tar file using tar.

Demo

I'd like to devote ~15-20 of one of our class meetings to have everyone run and try out each others' solutions in the CS Lab. If we cannot do this, then I'd like you and your partner to sign up for a 15 minute demo slot to demo your solution to me.