CSE 532 Spring 2008
Lab 0: "Card Dealer and Players"
Teams: please post the names of the 2 or 3 people on your team for Lab 0 to the
course forum by 11:59pm Sunday January 27, 2008 (after which any remaining team assignments
will be made by Prof. Gill)
Points: 5% of final grade
Submitted: by electronic submission to cse532@cec.wustl.edu
Due: by 11:59pm Sunday February 10, 2008
Purpose
The purpose of this assignment is to get you up to speed with using
basic wrapper facades for networked OO programming, as provided by
the ACE framework.
We will use these wrapper facades, and the design and implementation
skills you'll develop using them, repeatedly throughout the semester.
Whether or not you're already experienced with network programming,
it is well worth spending some time understanding how ACE organizes
the sockets API into wrapper facades, and how to use those wrapper
facades to best effect. An excellent place to start is with a careful
reading of chapters 2 and 3 in C++ Network Programming, Vol. 1.
Assignment
In this assignment you will write C++ client and server programs that
can:
- access underlying operating system resources and services such as sockets,
- connect to one another over a network,
and
- exchange messages in a coordinated manner.
We will start with a simple card game application in which a single server
(the dealer) will listen on a commonly known port for connection
requests from clients (players). When a client connects to the server, it will
tell the server in which games it wishes to participate.
The server will then add the client to one or more data structures,
which the server uses to associate players with particular card games. Each time a
client connects, the server will establish a new socket connection with that client,
send a join message to all the players already in the game
(and also to the player registered by the newly joining client - thus serving as its acknowldegement).
To do this please perform the following tasks:
Server
- Write a Dealer class (you are free to use additional classes if you'd like, say to separate
server concerns like socket connection management from dealer concerns like which players are in which games)
that maintains one or more data structures with:
- the names of the connected players,
- the socket handle through which to communicate with each player's client, and
- the names of the card games that each player has joined.
The data structure(s) should start out empty, and should be populated dynamically
as clients connect to the server. The Dealer class should
provide a default port at which it will listen for connection
requests, unless an overriding port is supplied on the program command
line.
- Write a simple main function to initialize the server program from the
command line, using switches with with the following syntax:
server -n <dealer name> [-p <server port>],
where:
<dealer name> is a string with no intervening spaces
(for example, -n SimpleDealer), and
<server port> is an alternative
port number at which the server should listen for connections (if specified).
The -p switch should be optional, and if one is not
specified the server should default to listening on some port
number specified by the Dealer class: please remember that ports
with port numbers up to 1024 are reserved for standard protocols
like telnet, ftp, ssh, http, and so on, so you should only use
port numbers greater than 1024 for your server's listening port.
Also, you may want to try a different port (and/or run ps
-u <uid> to see what processes you already have
running, some of which you may want to terminate via the kill command)
if the server fails to bind on a given port.
If the user fails to specify either the -n switch, or gives
another switch not recognized by your server program (you are free to
add any additional switches you think useful), your program
should print out a helpful syntax message before exiting, like
the following:
usage: server -n <dealer name> [-p <server port>]
- When the server is first initialized (after the command line
parameters have been processed) it should print out its dealer's name to the
standard output stream.
- The server should then listen on the
given (or default if none is given) port on the host machine where
it is running, by opening an Acceptor (the following code
fragment from the C++NP books is one very simple way to do this for Lab 0).
const int HOST_NAME_SIZE = 64;
ACE_INET_Addr server_addr;
ACE_SOCK_Acceptor acceptor;
char address_string [HOST_NAME_SIZE];
::gethostname (address_string, HOST_NAME_SIZE - 1);
if (server_addr.set (server_port_, address_string) < 0
|| acceptor.open (server_addr) < 0)
{
// ... perform the Server's other tasks ...
}
- Since clients and servers may run on different machines, it is
important to use the host name (or IP address) as well as the port in
setting the address on which the acceptor will listen, so that a
fully formed address:port string can be extracted from the server
to give to the client, in case the defaults are not used. To do
this, when the acceptor open call succeeds, you should then
obtain the server's listening address and port number as a string
from the address on which you asked it to listen, as in:
server_addr.addr_to_string (address_string, HOST_NAME_SIZE
- 1);
and print it out to the standard output
stream.
- It is also very important to check the return value from each of
the C++ calls made when initializing the Acceptor (or for that matter
any time a call that could potentially fail is made). If one of those
calls fails, the program should print an error message indicating
which call failed (and what the return value or the position of the failure in the
code indicates may have caused the failure), release resources, and shut down gracefully.
Design and debugging
hint: be careful with the next part of the program, as the server can
become deadlocked if its (only) thread is trying to read content
that will never arrive from the socket.
- The server should then repeatedly:
- accept a socket connection request from a local or remote instance of the
Player class;
- send its dealer's name to the client on the newly created connection (see
below for handshaking issues between the server and client - you may
want to terminate the name with some delimiter such as
a zero character, a space, an end of line etc.) ;
- read the client's player's name back from the newly created connection;
Note that the order in which these steps (and the corresponding steps in the client
portion of the assignment) are specified is designed to
avoid deadlocks - you are free either to have the server send its dealer name first
and then wait for the client to send its player name, or
if you prefer you can have the client send first, and then have the server reply.
- read the complete series of games from the client: how
you structure this protocol is up to you: two usual ways to do
this are either to
- send the number of (zero delimited, space delimited, etc.) names of card games that
will be transmitted, or the number of bytes in the entire sequence that will be transmitted, or
- use some kind of end of
list marker (i.e., some other string that cannot represent part of a card game's name
or the terminator of a card game's name should be used as a kind of "reserved word").
- if the player is not yet registered (for this lab you can choose to accomodate or reject
duplicate player registrations as you prefer), add the new player's name, socket handle, and
list of card game names to the server's data structure(s);
- for each of the card games the new client has joined, send a message to
each member of the game (and only to members of that game) indicating
the name of the game and the name of the newly joining player;
- print out the names of the players that are now in each card game on the server, and then
print out the server's dealer name, host, and port.
- To demonstrate your Dealer class, you should please run at least a couple of copies of the server
on a known host machine (or machines - make sure your servers can be run either on different
machines or on the same machine with different listening ports), and optionally with a designated port as in:
server -n SimpleDealer
server -n AnotherDealer -p 10070
where the first server defaults to listening on some other valid port than 10070.
Clients
- Write a Player class (as with the Dealer class, please feel free to use additional
classes, say to separate client and player concerns) that maintains a player name (a
string), a dealer name, a socket connection to the server, and a list of the games
it has joined. The Player class should provide a default port
and IP address at which it will contact the server unless an
overriding port and/or IP address is supplied to the program command
line.
- Write a simple main function to initialize the client program from the
command line, using switches with with the following syntax:
client -n <player name> -g <game name>
[-p <server port>][-i <server IP address or hostname>],
where:
<player name> is a string with no intervening spaces
(for example, -n Alice),
<game name> is also a string with no intervening spaces
(note that the names of multiple games can be specified on the command line,
each with a separate copy of the -g switch: for example, -g hearts -g poker), and
<server port> and <server IP address or hostname>
give an optional alternative
port number and/or IP address at which to contact the server.
If the user fails to specify the -n switch and at least one -g switch, or gives
another switch not recognized by your program (you are free to
add any additional switches you think useful), your client program
should print out a useful syntax message before ending gracefully, like
the following:
usage: client -n <player name> -g <game name>
[-p <server port>][-i <server IP address or hostname>]
- After it is initialized (when all command line switches have been processed), the client
should:
- open a socket connection to the server at the address and port for that
server (based on the defaults, but overridden by command line parameters if they
were specified - note that the server IP address should default to that of the host
where the client is running, and the clients and servers should use the same default port value),
- read the dealer's name from the server on the newly created connection and
print out a message to the standard output stream indicating the name of the server
to which it has connected.
- The client should then send its name to the server, and then send the server a list of
the games it wants to join: as noted above, how you structure the protocol for sending
the list (in particular, how to detect the end of the list) is up to you, but watch out
for cases where the server and client could have different expectations which can lead
to deadlock.
- After the client has sent its list of games, it should repeatedly read from the socket
and print out the join messages it receives from the server, to the standard output stream.
- To demonstrate your client class, you should first please start a copy of the server program
and then start several clients with different combinations of switches, as in:
client -n Alice -g hearts -g poker -p 10070
client -n Bob -g bridge -g poker -i hive.cec.wustl.edu
client -n Charlie -g hearts -g bridge
For initial debugging purposes you may want to try starting just a few clients, one at a time, to observe their
interactions with the server separately, but then for testing purposes you should run multiple clients and
servers at once, to test different combinations of the clients and servers being on the
same machine, or on different machines, to ensure that all combinations work reasonably.
Resources
There are a number of resources available that can greatly ease your
task in completing this assignment. This
project is designed to be very straightforward if you study and use
the Wrapper Facades mentioned in this section, and may be much more
difficult if you do not.
Also, there are several more mundane (but as in any software
project, important) areas where you can exploit tools and examples to
greatly speed your progress on this project and the ones later in the
semester. In particular, code fragments and Makefile examples are
provided as exemplars to follow or discard, as you choose.
Wrapper Facades
The following Wrapper Facades are provided by the ACE version
installed on the CEC Linux machines in the
~cse532/ACE_wrappers/ace/ directory (also can be viewed on the web at
http://classes.cec.wustl.edu/~cse532/ACE_wrappers/ace/) and are highly useful for this assignment:
- ACE_INET_Addr -- an internet domain socket abstraction
- ACE_SOCK_Acceptor -- an abstraction for accepting socket connection requests
- ACE_SOCK_Connector -- an abstraction for requesting network connections
- ACE_SOCK_Stream -- an internet domain stream socket abstraction
- ACE_Get_Opt -- an option-string parameterized abstraction for
parsing command line arguments
There is a very nice (and short) description of how to use the
Acceptor/Connector pattern in a very simple form (as much as we'll
need now, before we dig into the pattern in its full generality).
Code fragments for using Acceptor (as in the Server class) and
Connector (as in the Client class) are on pages 67 and 59
(respectively) of the C++NPv1 book:
- Schmidt and Huston
C++ Network Programming, Volume 1, Addison-Wesley, 2001.
The container classes described in Austern Chapter 16, particularly vector
and/or map, may be useful in implementing your server data structure(s)
to keep track of clients and the games to which each belongs, though you will
not be required to use the STL until Lab 1.
Makefile & Environment
The GNU C++ compiler (g++) is installed on the CEC Linux machines.
You will need to set a few environment variables (it may be convenient
to keep these in a small script file and/or simply set them in your
.cshrc or .bashrc file):
setenv ACE_ROOT /home/cec/class/cse532/ACE_wrappers
setenv LD_LIBRARY_PATH ${ACE_ROOT}/ace:${LD_LIBRARY_PATH}
You will need a Makefile that links appropriately with the ACE
libraries and include files. Please feel free to adapt or use the
following example Makefile (for folks who took
CSE 332 a.k.a. CS 342 with me previously, this may look familiar ;-).
What to Submit
README
When you have completed your program, please write up a description of
your solution in a text file called README.
The first section of your README file should include:
- the number of the lab (e.g., "CSE 532 Spring 2008 Lab 0")
- the names and e-mail addresses of all team members
- an overview of how your programs are designed,
- the Wrapper Facades you used or extended and how
they helped in your design and code, and
- insights, observations and questions you encountered while completing
the assignment.
The second section of your README file should provide detailed
instructions for how to:
- unpack
your files,
- build your programs, and
- run your programs.
Please
indicate any and all environment variable settings, etc. you are
assuming will be needed for this.
Important: I must be able to receive your e-mail, and
unpack, build, and run your programs using only the instructions in
your README file and the g++ version and other tools available on the
CEC Linux machines.
Electronic Submission
Please submit by e-mail to cse532@cec.wustl.edu a single
file containing: - your source code files,
- your Makefile,
- any example files you think useful (for example, a script with the parameters
you used to run the clients and server),
- output files from at least one significant test of your programs,
and
- your README file.
You can use the uuencode,
tar, and zip or gzip tools to
do this, as in the provided example Makefile.
Please send in a separate copy of your README file, with
clear instructions on how your submission file was produced and how to
unpack it using tools available on the CEC Linux machines.
Grading
Please treat this assignment as you would a commercial or research
product that you are shipping to a large group of customers. Please
take the time to test, document, and check your work to make sure all
parts are shipped, are of high quality, and behave resonably under a range of different
operating conditions. Grading will focus both
on the quality of your solution and on the quality of the product
itself, as it arrives at the cse532 account.
To ensure the best possible result (and accordingly the highest
possible grade), please pay attention to the following issues:
Correct Compilation and Operation
Your program must compile and run correctly on the CEC Linux machines
using the Makefiles and source files you provide. Please make sure
your program compiles without warnings, and you might even want to try
running it on different compilers and fixing all the warnings for
each. Problems with Makefiles will be handled similarly to missing
files, with a 5 point deduction if you need to supply a new Makefile
in order for me to build your solution on the CEC Linux machines.
Design Quality
Design decisions are largely yours to make, and as long as the design
is concise, coherent, consistent, and addresses all design forces in the
assignment, you will receive full credit. One key area to consider
is whether each abstraction in your design does a single job, does
that job completely, and collaborates appropriately with other
abstractions. Minor deductions (1-3 points, but please be aware minor
deductions can add up) will be made for abstractions
that are unnecessarily large or have inappropriate inter-dependencies.
Major deductions (5-15 points) will be made for larger problems like
neglecting key requirements of the assignment.
Implementation Quality
How you implement your solution is again up to you, and I will take
into account different approaches to implementation. Minor deductions
will be made for things your program gets away with but are not good,
like neglecting to check the return value from a system call or
Wrapper Facade method (i.e., the program may have problems under
special conditions), or calling exit(1) from inside a class method
rather than providing a clean termination path. Major deductions
will be made for problems that produce incorrect or extremely
inefficient behavior of the program. The former kinds of errors
should be eliminated during your coding and code review phases, and
the latter kinds of errors should be eliminated during your testing
phase, which should include running different combinations of clients
and servers on the same machine and on different machines.
Coding Style
Different coding styles will also be accepted, as long as they are
clear, readable, and consistent. Please code clearly with both the
reader of the code and the user of the program in mind. Please use
consistent indentation and placement of braces, and comment your code
thoroughly. Use whitespace liberally - it's free and it makes it a
lot easier to read your code. When grading, I will add tagged
comments to the code indicating areas where I found particular issues
to mention, whether or not points are deducted. Only minor deductions
will be made for each style issue, except in extreme cases.
Documentation
Please make sure you provide adequate instructions on how to unpack,
build, and run your program. Also, please make sure to document your
solution. Minor or even major deductions will be made for inadequate
explanation of how your solution does what it does, why you made key
design choices, or how the user (or grader) can successfully build and
run your program. Even if how you did something is obvious to you,
please assume it is not obvious to the reader.
Missing Files
Missing files in the delivered software make it difficult or
impossible to evaluate your solution. An automatic 5 point deduction
will be applied for each missing or corrupted file that is submitted
later on request.
Late Submission
Labs submitted within 24 hours of the deadline will be graded with an
automatic 10 point deduction. Labs submitted more than 24 hours after
but within 48 hours of the deadline will be graded with an automatic
20 point deduction. Labs submitted after 48 hours from the deadline
will not be graded, except under extenuating circumstances. If you are running late completing the
assignment, please let me know about the trouble as soon as possible,
and please turn in as much as you can before each deadline so I can
give at least some credit for the work you have done.