Assignment 2
Supplemental pages
- Assignment FAQ
- Client-server communication lecture notes
- Client-server communication lecture slides
- socket programming demo code [tar file] [zip file]
Objective
Your goal in this assignment is to write a set of sockets-based file copying programs. You'll write get and put clients and a server. The server will accept or reject requests based on the IP address of the host issuing them. Your program has to follow the protocol described in this document. You'll
Don't be intimidated by the length of this write-up. The HTML for this page is longer than the C code for the assignment!
Languages
You may do this assignment in either C, C++, or Java.
Background
First be sure that you are familiar with sockets programming. To simplify matters, you can obtain a couple demo client/server programs from http://www.cs.rutgers.edu/~pxk/rutgers/src/socketdemo.tar. Extract the files with:
tar xvf socketdemo.tar
This will create a directory called socketdemo with a README file and the following subdirectories:
-
send_c - just about the simplest C TCP sockets program. Accepts data from a socket and prints it to the standard output.
-
send_j - the same thing in Java.
-
echo_c - an echo server: it reads lines from a socket and echos them back to the requestor. A process is forked off for each client session.
-
echo_j - almost the same thing in Java. There are two versions here: A single threaded one that works with one client at a time and a multithreaded version.
-
cmd_c - a somewhat messier C TCP sockets program. The accepts requests for commands, processes them, and sends results back to the client. A process is forked off for each client session.
-
jsocketdemo - pretty much the same thing in Java, except that a thread is spawned for each server request.
Each of these directories contains a README file
describing what the program does, how to compile it, and how to
run it. All directories contain a Makefile and
the C versions contain a Makefile for Solaris and
a Makefile.linux for Linux systems. If you use cygwin
on Windows systems, you should be able to use the Linux Makefile.
If you have time to look at only one of these, echo_c is the simplest one.
Specifications
You will write three program for this assignment: a get client, a put client, and a server.
The server
The server executable will be named server (Server in java).
It accepts a port number on the command line following a -p flag. For example:
will start the server listening to port 1234. If this parameter is missing, you should fall back to some hard-wired port number (in the range 1024...4999 that will be the same as in your clients). If using C, use getopt to parse command-line arguments. If you haven't used getopt before, see the man page; it has an example. Any other options or arguments should produce an error message.
The server will perform the following sequence of operations:- Open a socket and set it up for listening - follow the example in the demo code (don't forget to setsockopt to SO_REUSEADDR as in the sample code in the demo and lecture notes if you are using C/C++). Your program does not have to be multithreaded and needs to only process one request at a time.
-
After accepting a connection, the first thing that
the the server will receive from the client will be a single byte containing
the ASCII character P or G:
P put a file G get a file - After that the client will send a string containing the file name, which may be up to 128 bytes long. For the network protocol, a string is defined as a byte containing the length of the string followed by the bytes of the string. The string "abc", for example, will be sent as a sequence of the following bytes: 3, 'a', 'b', 'c'.
- Handling put requests
-
The client will send a 32-bit (four byte) value containing the number
of bytes in the file that will be sent. This number will be sent in the IP network standard format - big
endian, with the most significant byte sent (and received) first. In C, you can use the macros
htonl(host to network - long) andntohl(network to host - long) to convert. -
[Optional: extra credit]
The server will first validate the IP address of the requestor. A list of valid addresses is stored in a file
called
accept. This is a plain-text file containing zero or more IP addresses, one per line. Each address is in the conventional dot-delimited format for IPv4 addresses (e.g., "128.6.4.1"). If the source address is in this file, the protocol can proceed. Otherwise, the server sends back a rejection message. A rejection message is the ASCII character 'N' followed by a string containing the message. As mentioned in step 5, the format of a string is a byte containing its length followed by the characters of the string (no terminating 0 character). -
The server validates the file name. It has to be under 128 characters long and contain no "/" characters. All characters should be printable characters (see the isprint man page). If you don't want to use that, just ensure that the character range is ≥ " " (space) and ≤ "~".
If the validation fails, the server sends a rejection message to the client. A rejection message is the ASCII character 'N' followed by a string containing the message. As mentioned in step 5, the format of a string is a byte containing its length followed by the characters of the string (no terminating 0 character).
- Now the server tries to create the local file with the given name. If that fails, it sends back a rejection message to the client.
- If the local file was successfully created, the server is ready to get the contents. It sends back an acknowledgement to the client. This is a single byte containing the ASCII character 'Y'.
- The server now loops, reading chunks of data from the client and writing it to the local file until there's no more data to receive. If the number of bytes received (and written) does not match the number of bytes that the serve send with the request, print a message on the server stating so.
-
The client will send a 32-bit (four byte) value containing the number
of bytes in the file that will be sent. This number will be sent in the IP network standard format - big
endian, with the most significant byte sent (and received) first. In C, you can use the macros
-
[Optional: extra credit]
The server will first validate the IP address of the requestor. A list of valid addresses is stored in a file
called
accept. This is a plain-text file containing zero or more IP addresses, one per line. Each address is in the conventional dot-delimited format for IPv4 addresses (e.g., "128.6.4.1"). If the source address is in this file, the protocol can proceed. Otherwise, the server sends back a rejection message. A rejection message is the ASCII character 'N' followed by a string containing the message. As mentioned in step 5, the format of a string is a byte containing its length followed by the characters of the string (no terminating 0 character). - The server validates the file name. It has to be under 128 characters long and contain no "/" characters. If the validation fails, the server sends a rejection message to the client. A rejection message is the ASCII character 'N' followed by a string containing the message. As mentioned in step 5, the format of a string is a byte containing its length followed by the characters of the string (no terminating 0 character).
- The server now attempts to open the local file that is being requested. If the open fails, it sends a rejection message to the client.
-
if the file open succeeds, the server
sends back an acknowledgement to the client. This is a single byte containing the ASCII
character 'Y'. The server then sends the
sends a 32-bit (four byte) value containing the number
of bytes in the file. This number will be sent in the IP network standard format - big
endian, with the most significant byte sent (and received) first. In C, you can use the macros
htonl(host to network - long) andntohl(network to host - long) to convert. - The server now loops, reading chunks of data from the file and sending it to the client until there's no more data to read.
The put client
The put client is named put (Put in Java) and accepts the remote and local file names as command-line parameters. It reads the contents of the file and sends them to the server where the server will create a file with the same name and contents. If the names are missing, you should print an error and exit the program.
It accepts two optional parameters: the port number and the server host name. The port number is indicated with a -p flag and the server host name is indicated with a -h flag:
The brackets indicate that the parameters are optional. They may be present in either order. If programming in C, you should use getopt to parse the command line.
The default hostname should be the string localhost. The default port should be the same as used in the server (not "remus", for example!).
- The client opens the specified file (exiting with an error message if the open fails).
- The client should follow the protocol dictated by the server. It sends the request (P) followed by the file name and followed the number of bytes in the file. As described earlier, the name is a string that is prefixed with a byte containing the length of the text and the file length is a big-endian 32-bit value.
- The program now reads a response. A byte containing a 'N' means that the request was rejected. The string following contains the rejection message.
- A response containing a 'Y' means that the client can send the data to the server.
- Finally, the client reads data from the the opened file and sends it to the server block by block until it reaches the end of the file, closing the connection when the end of file is reached.
- Close the local file, close the socket, and exit the program.
The get client
The get client is named get (Get in Java) and accepts local and remote file names as command-line parameters. It sends the remote file name to the server, which will then send the contents back to the client. The client writes those contents to a local file. If the names are missing, you should print an error and exit the program.
It accepts two optional parameters: the port number and the server host name. The port number is indicated with a -p flag and the server host name is indicated with a -h flag:
The brackets indicate that the parameters are optional. They may be present in either order. If programming in C, you should use getopt to parse the command line.
The default hostname should be the string localhost. The default port should be the same as used in the server (not "remus", for example!).
- The client should follow the protocol dictated by the server. It sends the request (F) followed by the file name. As described earlier, the name is a string that is prefixed with a byte containing the length of the text.
- The program now reads a response. A byte containing a 'N' means that the request was rejected. The string following contains the rejection message.
- A response containing a 'Y' means that the client can send the data to the server. Following that is the length of the file that will be received (four bytes).
- Finally, the client reads data from the the opened file and sends it to the server block by block until it reaches the end of the file, closing the connection when the end of file is reached. If there's a mismatch between the number of bytes actually received versus the number of bytes expected, print an error message to that effect.
- Close the local file, close the socket, and exit the program.
Protocol summary
Get protocol
Put protocol
A few notes
- Remember that sockets have no record boundaries. One write on the server does not necessarily translate to one read on the client. Even if this appears to be the case, it is not guaranteed and you cannot assume this behavior.
- Do not attempt to read the data a line at a time - there should be no assumption that the file data is ASCII text or contains any newlines.
- You should not buffer the entire file in memory: just establish the socket and send the blocks as you read them.
Submission
Create a plain text file named id that contains your name and RUID number. Please write your name in a leading comment in your code as well.
You will submit everything I need to compile your program. This will, at minimum, be three files that implement your two clients and server. If you're programming in C, I'd recommend that you create a file with common functions, such as establishing a socket connection, that will be shared by all.
You may submit a Makefile so that I can run make to compile your program (see some basic notes on makefiles). Otherwise, be sure to submit a README file explaining how to compile and run your program.
Submit only what is required. Do not submit object files, executables, or temporary editor files.
To submit the assignment, you will create a tar archive of all the files that are needed to compile the program, (including the makefile, if any):
This assumes that the directories have been purged of all the files that you do not want to submit (class files, temporary files, etc.).
Hand the assignment in using the web-based handin procedure. Go to https://handin.rutgers.edu/. If you never used web-based handin, check out the instructions at https://handin.rutgers.edu/handin/manual/stud/stud.html . The project name for this assignment is Assignment 2.
Before sending the file to me, make sure that all the components are there. If I can't compile any part, you will get no credit.
Pre-submission notes
Important: please check my notes on submitting assignments before you hand in your assignment.