CSCI 4210 | Operating Systems
CSCI 6140 | Computer Operating Systems
Homework 4 (document version 1.0)
Sockets Programming using C
Overview
• This homework is due by 11:59:59 PM on Monday, April 24, 2017.
• This homework will count as 9% of your nal course grade.
• This homework is to be completed individually. Do not share your code with anyone else.
• Your code must successfully compile and run on Submitty, which uses Ubuntu v14.04.5 LTS.
• You must use C for this homework assignment, and your code must successfully compile
via gcc with absolutely no warning messages when the -Wall (i.e., warn all) compiler option
is used. We will also use -Werror, which will treat all warnings as critical errors.
• Note that the gcc compiler is version 4.8.5 (Ubuntu 4.8.5-2ubuntu1~14.04.1).
Homework Speci cations
In this nal homework assignment, you will use C to write server code to implement a data storage
server using server sockets.
For your server, clients connect to your server via TCP or UDP. For TCP, clients connect via a
speci c TCP port number (i.e., the listener port); this TCP port number is the rst command-line
argument to your server. For UDP, clients send datagram(s) to a speci c UDP port number; this
UDP port number is the second command-line argument to your server.
Your server must not be a single-threaded iterative server. Instead, your server must use either
multiple threads or multiple child processes. Further, to support both TCP and UDP at the
same time, you must use the select() system call to detect incoming TCP connections and UDP
datagrams.
As with previous assignments, your server must also be parallelized to the extent possible. As such,
be sure you handle all potential synchronization issues.
Note that your server must support clients implemented in any language (e.g., Java, C, Python,
etc.); therefore, be sure to handle only streams of bytes as opposed to speci c language-speci c
structures.
And though you will only submit your server code for this assignment, feel free to create one or
more test clients. Test clients will not be provided, but you may share test clients with others via
Piazza. Also note that you can use netcat (or nc) to test your server.
Application-Layer Protocol
The application-layer protocol between client and server is a line-based protocol (see the next page).
Streams of bytes (i.e., characters) are transmitted between clients and your server.
The speci cs of the application-layer protocol depend on whether clients use TCP or UDP. For
TCP, clients connect to the server and can add and read les; clients can also request a list of les
available on the server.
Since UDP is connectionless, UDP clients are only able to request a list of les available on the
server.
For TCP, once a client is connected, your server must handle as many client commands as necessary,
closing the socket connection only when it detects that the remote client has closed its socket. This
is not the case in UDP since UDP is connectionless.
For both TCP and UDP, a dedicated child process or child thread must handle the TCP connection
or UDP datagram.
All regular les must be supported, meaning that both text and binary (e.g., image) les must be
supported. To achieve this, be sure you do not assume that les contain strings; in other words,
do not use string functions that rely on the \0 character. Instead, rely on byte counts.
Files must be stored in a directory called storage. As your server starts up, check to be sure this
directory exists; if it does not, report an error to stderr and exit. In general, your server can either
keep track of stored les via a (shared) structure or use system calls to check for the existence of
stored les. Note that you will have synchronization issues to resolve here (e.g., what happens
when a client attempts to read a le as it is being written by another client?).
On Submitty, you can assume that the storage directory is initially empty.
Finally, subdirectories are not to be supported. A lename is simply a valid lename without
any relative or absolute path speci ed. More speci cally, we will test using les containing only
alphanumeric and . characters; you may assume that a lename starts with an alpha character.
2
The application-layer protocol must be implemented exactly as shown below:
SAVE \n
-- only allowed over TCP
-- save file on the storage server
-- if is invalid or is zero or invalid,
return "ERROR INVALID REQUEST\n"
-- if the file already exists, return "ERROR FILE EXISTS\n"
-- return "ACK\n" if successful
READ \n
-- only allowed over TCP
-- server returns bytes of the contents of file ,
starting at
-- if is invalid or is zero or invalid,
return "ERROR INVALID REQUEST\n"
-- if the file does not exist, return "ERROR NO SUCH FILE\n"
-- if the file byte range is invalid, return "ERROR INVALID BYTE RANGE\n"
-- return an acknowledgement if successful; use the format below:
ACK \n
LIST\n
-- allowed over TCP and UDP
-- server returns a list of files currently stored on the server
-- the list must be sent in alphabetical order (case-sensitive)
-- the format of the message containing the list of files is as follows:
... \n
-- therefore, if no files are stored, "0\n" is returned
For any other error messages, use a short human-readable description matching the format shown
below. Expect clients to display these error descriptions to users.
ERROR \n
Note that all commands are case-sensitive, i.e., match the above speci cations exactly. Make sure
that invalid commands received by the server do not crash the server. In general, return an error
and an error description if something is incorrect or goes wrong.
You can assume that the correct number of bytes will be sent and received by client and server. In
practice, this is not a safe assumption, but it should greatly simplify your implementation.
Be very careful to stick to the protocol or else your server might not work with all clients (and with
all tests we use for grading).
3
Output Requirements
Your server is required to output one or more lines describing each command that it executes.
Required output is illustrated in the example below. The child IDs shown in the example are
either thread IDs or process IDs. And as per usual, output lines may be interleaved.
bash$ ./a.out 9876 9889
Started server
Listening for TCP connections on port: 9876
Listening for UDP datagrams on port: 9889
Rcvd incoming TCP connection from:
[child 13455] Received SAVE abc.txt 25842
[child 13455] Stored file "abc.txt" (25842 bytes)
[child 13455] Sent ACK
[child 13455] Received READ xyz.jpg 5555 2000
[child 13455] Sent ERROR NO SUCH FILE
[child 13455] Client disconnected
...
Rcvd incoming TCP connection from:
[child 11938] Received SAVE def.txt 79112
[child 11938] Stored file "def.txt" (79112 bytes)
[child 11938] Sent ACK
[child 11938] Client disconnected
...
Rcvd incoming UDP datagram from:
[child 17732] Received LIST
[child 17732] Sent 2 abc.txt def.txt
...
Rcvd incoming TCP connection from:
[child 19232] Received READ abc.txt 4090 5000
[child 19232] Sent ACK 5000
[child 19232] Sent 5000 bytes of "abc.txt" from offset 4090
[child 19232] Received LIST
[child 19232] Sent 2 abc.txt def.txt
[child 19232] Client disconnected
...
Submission Instructions
To submit your assignment (and also perform. nal testing of your code), please use Submitty. The
URL is on the course website.
Be sure you submit only your server code (i.e., do not submit any client code).
4