Each exercise this quarter is rated on a integer scale of 1 – 5, inclusive, with 1 being the "least time-consuming" and 5 being the "most time-consuming".
This difficulty scale is meant as a rough guide for you in predicting the amount of time to set aside for each exercise as you balance the work required for 333 with your other obligations. However, it is necessarily imperfect as everyone's set of circumstances and experiences with the exercises differ. If your experience with an exercise does not align with its rating, that is not a reflection of you or your abilities.
Write a C++ program that connects (via TCP) to a server specified by a user-supplied hostname and port number, sends the bytes of a specified local file to the server, and then closes the connection and exits. Your program should accept the command-line arguments in the following order:
We provide WrappedRead() and
WrappedWrite() for transferring the file. These
functions use the POSIX read() and
write() functions and handle recoverable errors and
partial writes for you.
An example execution of the completed application is:
$ ./ex8 localhost 5555 test.txt
The starter contains four relevant files:
SocketUtil.h : Provides the public
interface for various client-side networking utility functions.
Do not modify this.SocketUtil.cc : Contains empty
implementations of LookupName() and
Connect(). The other utility functions are provided
for you. You complete part of this.ex8.cc : Contains an empty
main function for the client-side networking
program. You complete this.Makefile : Provided for your
convenience in compiling the executable ex8.
Do not modify this.SocketUtil.cc and
ex8.cc so you should not modify the other
source files.
Complete the exercise in this order:
SocketUtil.cc by implementing
LookupName(), which resolves the hostname and
initializes a socket address with the requested port, and
Connect(), which creates a TCP socket and connects
it to that address.main() in ex8.cc. It
should validate the command-line arguments, open the local file,
use your socket utility functions to connect to the server,
transfer the entire file, and close all open file descriptors.
We provide the implementations of WrappedRead() and
WrappedWrite(). Use these functions when transferring
the file and do not modify them.
A network address has a different concrete C structure depending
on its protocol: IPv4 uses sockaddr_in, while IPv6
uses sockaddr_in6. A sockaddr_storage is a
generic container that is large enough and properly aligned to
hold either kind of address. This lets LookupName()
return one address without requiring its caller to know in advance
whether DNS will produce IPv4 or IPv6.
The ss_family field records which address family is
actually stored. Because IPv4 and IPv6 addresses have different
sizes, the address must travel with its length; that is why
LookupName() also fills ret_addrlen and
Connect() receives addrlen.
POSIX functions such as connect() accept the common
interface type sockaddr*. When passing a
sockaddr_storage to one of these functions, cast its
address to the required sockaddr* type and pass the
matching length. The family and length tell the operating system
how to interpret the bytes in the generic container.
Feel free to adapt sample code from lecture and section as part of your solution if it helps, but be sure you understand what your code does when you're done.
Require exactly three command-line arguments after the program name. The hostname must resolve to an address, the port must be a decimal integer from 1 through 65535, and the local file must be readable. Reject input that does not meet these requirements.
Check the return value of each function that can fail and return a
failure status when an operation cannot be completed. Print a
useful message to standard error and return
EXIT_FAILURE from main(). The provided
wrappers already retry read() and
write() after recoverable errors
(EAGAIN and EINTR). Make sure that you
close open file descriptors in all possible cases,
including when connect() or a file transfer fails.
Build the ex8 executable with the provided
Makefile:
$ make
You can remove generated files with make clean.
To test your program, you will need to set up a server to receive
the data that your ex8 executable will send.
The recommended way to do this is using the nc
utility:
$ nc -l <port> > output.bytes
This command will run a netcat listener (-l) on port
<port> (e.g., 5555), which needs
to match the port you provide to the ex8 executable,
and redirect any received bytes to the file
output.bytes.
Note that this will create the file if it didn't exist or overwrite
if it does exist.
nc will exit once it has processed a single
connection, so you'll need to rerun nc each time you
test your client.
If you are running the server on the same computer/host that you
are testing your code on, you can use the special loop-back IP
address 127.0.0.1 or localhost to refer to the
same host.
Please note that each attu (e.g.,
attu1, attu2) counts as a separate host,
so if you are testing on attu, both client and server
must be running on the same one for this to work.
To log into a specific attu machine, run:
$ ssh <netid>@attu<#>.cs.washington.edu
where <#> should be replaced by a number 1 – 8.
After the client and server exit, compare the received file with
the original. cmp produces no output when the files
contain exactly the same bytes:
$ cmp test.txt output.bytes
Test more than the successful example. Your program should also handle an empty file, a nonexistent file, a missing argument, an invalid hostname or port, and a server that is not listening.
Do not modify SocketUtil.h, the provided wrapper
implementations, or any function signature. The autograder builds
your code against the provided interface.
The POSIX networking API requires some C interfaces, but use C++
idioms elsewhere. For example, use stream output instead of
printf() and C++ casts instead of C-style casts.
Submit your work by creating an ex8-submit tag in your exercise repo before the deadline. These files must live at the exact paths below, including capitalization:
ex8/ex8.ccex8/SocketUtil.cc
Other files in ex8 are ignored, so you may keep your
test files there.
For full credit, your submission must:
Makefile.valgrind..cc files with your name(s) and CSE or UW email
address(es).cpplint.py.