CS 85: Distributed Systems, Spring 2008, Swarthmore College, Professor Newhall

Old Skool Instant Messaging

Due: Thursday, Feb. 7 before 11am



Project introduction
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 Introduction

Prior to instant messaging systems, there was Unix talk. The talk daemon (like a server) runs on computers in a distributed system and listens for talk requests from users on remote computers. The talk daemon helps set up a talk session between a local and remote party, and then the two users have a text interface for instant message-like communication. talk is not as transparent as today's IM systems, because the user initiating the conversation needs to know the machine name (its IP) and the user name of the person to whom they want to communicate.

You can try out talk on our systems. For example, if user jo on machine foo wants to talk with user mo on machine blah, jo will enter the command:

% talk mo@blah
talk on blah sends a message to the talk daemon (talkd) on foo to initiate setting up a talk connection between jo and mo. The talk daemon of foo will send a message to the console window notifying mo how to respond to start a talk session with jo. For example, mo would see something like this:
Message from Talk_Daemon@foo at 13:36 ...        
talk: connection requested by jo@foo 
talk: respond with:  talk jo@foo      
Then mo would type the following to finish the talk connection and begin remotely talking with jo:
% talk jo@foo

For this assignment, we are going to implement a system that is similar to Unix talk (we will simplify some of the connection issues associated with talk). The goal of this assignment is to give you some practice writing an interface specification, and writing a client and server application using TCP sockets. You should write your solution as a C or C++ program that makes Unix system calls to create, set up a connect, read, write, and close a socket.

Because one goal of this assignment is to develop an interface for talk clients and servers, I'd like to have at least 3 different groups implementing a solution so that we can test if different group's clients and servers can speak to each other). You should work with one or two partners on this assignment, and I'd like at least one partner in each group to be someone who has taken CS45 (Jeff, Adam, Trilok, or Drew).


Project Description

For this assignment, you will write a client-server application that implements a simplified instant messaging system. The client and server will use TCP sockets to communicate. A client will connect to a remote server, after the connection phase, the client and server will act as the two parties in the talk session. When a client is done talking, it will send the server a special termination message, and exit. The server program WILL NOT exit after the talk session, but will instead wait for the next remote client to connect and talk with it (the server runs "forever").

During the talk session, the client and server alternate turns waiting for a message from the other (which will be sent a line of input at a time) and sending a message to the other. The client should be the first one to send a message (a line of input). Note that this is different than talk where both parties can "simultaneously" send and receive messages from the other.

For this assignment, we will only worry about the server being able to handle one client connection and talk session at a time (for the next assignment we will add multiple, simultaneous connections).


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 IP of a particular machine name, use nslookup:
%  nslookup curry
  ...
  Address  130.58.68.36

% ./talk_client 130.58.68.36   # start the client and try to connect to server
                               # running on curry (IP 130.58.68.36)

# when the connection succeeds, the client program should print a prompt 
# for user to enter a line of text to send to the server:

enter next message:  hello there

# then send the "hello there" string to the server and wait for the server
# to send a response.  When the client receives the server's response,
# it should print it out, and then ask the user if he/she wants to continue,
# and if so, print out the prompt for the user to enter the next line of 
# test to send to the server:

talk server response:  hi, what's up?
enter next message:

# the client repeats printing the prompt, reading input, sending ...
# when the user enters "hangup" to the "enter next message" prompt
# a special message should be sent to the server indicating that this
# talk session should be terminated, and the client should exit
The server will behave like the following after the initial TCP connection is set up:
# the server first waits to receive the first line of text from the client,
# and when it receives it, prints it out, and enters a prompt for the 
# user on the server side to enter the next message to be sent to the client
# (the server cannot terminate the conversation, only the client side can):

talk client says: hello there
enter next message: hi, what's up?

A sample session

Below is sample output of two different client sessions with the same server (the output is in terms of what each one sees as it handles the talk session). Note, that the server is printing out the client's IP and port number (you don't have to do this, and your output doesn't need to be identical to mine, but the behavior should be the same):
# client 1 on first machine connects to the talk server

 % ./cs85talk_client 130.58.68.62
 enter next message: hello
 talk server response is: hi there
 enter next message: what is happening?
 talk server response is: nothing, what is up with you?
 enter next message: nothing, I guess good bye
 talk server response is: bye bye
 enter next message: hangup


# client 2 on a different machine connects to the talk server

% ./cs85talk_client  130.58.68.62
enter next message: hello?
talk server response is: hi, who are you?
enter next message: I'm jo schmoe, who are you?
talk server response is: I am the all powerful talk server!!!!
enter next message: oh, well in that case, so long.
talk server response is: so long.
enter next message: hangup

# server (note when the conversation with the first client ends and the second begins)

./cs85talk_server
client (130.58.68.158,35804) sent message: hello
enter next message: hi there
client (130.58.68.158,35804) sent message: what is happening?
enter next message: nothing, what is up with you?
client (130.58.68.158,35804) sent message: nothing, I guess good bye
enter next message: bye bye

client (130.58.68.166,34479) sent message: hello?
enter next message: hi, who are you?
client (130.58.68.166,34479) sent message: I'm jo schmoe, who are you?
enter next message: I am the all powerful talk server!!!!
client (130.58.68.166,34479) sent message: oh, well in that case, so long.
enter next message: so long.

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  cs85talk.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.).

Once you have done some background reading about socket programs, then next step is to define the interface for your talk client and server. Since I want all student's clients to be able to communicate with all student's servers, as a class you will develop the interface for the client and server. I'll give you time at the end of class on Thursday to designing the API between client and server programs. The API will define how clients and server communicate different types of messages to each other, it will also define how each side knows when the contents of the next message have been received (for example, for some systems, fixed-length messages are sent between client and server, so each party knows when it has read enough bytes of the next message it is receiving, in other systems variable length messages are supported, and then the sender typically send the length of the message it is about to send first, so that the server knows how much to receive).

After the TCP connection is set up between a client and server, the client can sent two types of messages to the server, each one resulting in a different action by the server (one is an "I'm exiting message", the other is a "here is my next message to display" message followed by the line of text to display). Typically, when the server needs to interpret the type of a message in oder to know how to respond, the client sends a special short message (called a message tag) indicating which type of message it is sending, and then if need be, sends the rest of the message of that type to the server. The server, will first receive the message tag, and then invoke a routine for handling that type of message.

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 connections, 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 details of project:

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));

understanding return values from system calls

When system calls fail, they almost always return -1 (see the man page to check return values for individual calls). They also set a global variable, errno, to an error number indicating the type of error that occurred (also see the man page for ERRX values). You can call perror("some string") to print out a error specific message for the errno that was returned, you can also printout the value of errno, and then look in /usr/include/errno.h header files for the definition of that error number (most error values are defined in /usr/include/asm-generic/errno.h and /usr/include/asm-generic/errno-base.h, but there are other errno.h files in the /usr/include subdirectories and sometime you need to poke around a bit to find the right one.)

getting info about other end of the socket

use getpeername and inet_ntoa to get information about the other side of a socket connection.
struct sockaddr_in  name;

namelen = sizeof(struct sockaddr_in);
if(getpeername(sock_fd, (struct sockaddr *)(&name), &namelen)) {
  perror("getpeername");
  close(sock_fd);
	return -1; 
} 
printf("client port %d ip %s\n", name.sin_port, inet_ntoa(name.sin_addr));

getting socket and IP info

# to get my machine's IP addr:

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


# info for tcp sockets on  this 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

readline for reading in input entered by the user

When the user on the client and server side enters a new line of text to send to the other side, your program will have to read in that line of text from stdin (the keyboard input). The readline library (which is being linked in by the starting point makefile) provides an easy interface for reading in input in C. See the man page for readline for more information.

forcing printf output to the screen

printf output is buffered and may not show up to the terminal until the program has executed many instructions past the printf stmt. To force printf output to stdout, you can call fflush:
printf("hello there");
fflush(stdout);    // force all buffered printf output to stdout

getting the state of a process

# one way to find state of cs85talk_server processes:
ps -A | grep cs85talk_server
27318 pts/2    00:00:00 cs85talk_server
27321 pts/2    00:00:00 cs85talk_server

# if you see a listing like this, then you are not correctly handling process exits
27321 pts/2    00:00:00 cs85talk_server <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 a 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

We will devote ~20 at the end of class on Thursday to have everyone run and try out each others' solutions in the overflow Lab.


Except as otherwise noted, the content of this presentation is licensed under the Creative Commons Attribution 2.5 License.