Assignment 2

Due Monday, March 2, 2009 5:00 pm Wed, March 4, 2009 8:00 pm on-line

Supplemental pages

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.

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, please go through the TCP/IP Sockets Programming Tutorial. This includes a few simple programs and you can use snippets of code from there to get your code up and running.

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:

server -p 1234

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:
  1. 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.
  2. After accepting a connection, the server will receive a line of text that contains a request to either get a file or put a file. Line-oriented input is used in this case to allow you to test the program using a client such as telnet. A line is defined as text that is terminated by a newline character ('\n', ASCII 10). Carriage return characters ('\r', ASCII 13), which are generated by some telnet sessions or PCs, are ignored.

    This line of text that is received by the server is one of two possible messages:

    put filename
    get filename

    The put and get commands are followed by whitespace — one or more space (' ') or tab ('\t') characters — and then followed by a filename. The filename is ASCII text that identifies the file that will be either sent or requested.

    The filename should be no more than 128 bytes and cannot contain slash characters ('/').

  3. Handling put requests
    1. If the first line is a put request, all bytes after the newline comprise the data of the file that will be stored on the server.
    2. The request will either be accepted or rejected. An acceptance message is a newline-terminated string containing the string "ok". A rejection message is a line of ASCII text that contains the string error followed by a space and then a string containing the message.

      ok
      error error_message_text

    3. [Optional: extra credit] The server will first validate the IP address of the client. 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.
    4. 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 isprint, just ensure that the character range is ≥ " " (space) and ≤ "~".

      Optional:The filename may be surrounded by doublequote characters (e.g., "my file"). The quote characters will not be part of the filename. An embedded quote in a file must be prefixed by a backslash character ('\'). For example, "my \"file\"" will result in file named my "file".

      If the validation fails, the server sends a rejection message to the client.

    5. 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.
    6. If the local file was successfully created, the server is ready to get the contents. It sends back a success message to the client.
    7. 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.
  4. Handling get requests
    1. [Optional: extra credit] The server will first validate the IP address of the client. 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.
    2. 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 isprint, just ensure that the character range is ≥ " " (space) and ≤ "~".

      Optional:The filename may be surrounded by doublequote characters (e.g., "my file"). The quote characters will not be part of the filename. An embedded quote in a file must be prefixed by a backslash character ('\'). For example, "my \"file\"" will result in file named my "file".

    3. 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.
    4. If the local file was successfully opened for reading, the server is ready to send the file. It sends back a success message to the client.
    5. 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.
  5. The server closes the file and the socket and returns to accept the next connection.

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, it should print an error and exit.

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:

put [-h hostname] [-p portname] local_filename remote_filename

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

  1. The client opens the specified file (exiting with an error message if the open fails).
  2. The client should follow the protocol dictated by the server. It sends the request: a line containing put followed by the file name.
  3. The program now reads a response. As described earlier, the response is a single newline-terminated string containing either ok or an error message. If the response is an error message, print the message and exit.
  4. Now, 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. When it reaches the end of file, it closes the connection on the socket.
  5. 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, it should print an error and exit.

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:

get [-h hostname] [-p portname] remote_filename local_filename

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

  1. The client should follow the protocol dictated by the server. It sends a line containing get request followed by the file name.
  2. The program now reads a response. As described earlier, the response is a single newline-terminated string containing either ok or an error message. If the response is an error message, print the message and exit.
  3. Now, 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.
  4. Close the local file, close the socket, and exit the program.

Protocol summary

get protocol

Get protocol

put protocol

Put protocol


A few notes

  1. 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.
  2. While you want to send and process the request and ok/error responses as lines, the file contents can be arbitrary binary data. 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.
  3. 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):

tar cvf a2.tar id *.c *.h Makefile
or
tar cvf a2.tar id *.java README

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.