Program 2
Card Dealer
Now that you've got a client implemented to talk to a server, it's time to
move to the other end of the socket and implement a server. We'll also mix
things up a little and use UDP instead of TCP for this one. To prepare, look
at the sections on Datagram Services and Datagram Example in
Gary's Unix Network Programming Manual.
Implementing a real server based on a published RFC is too much work for this
stage of the game, so you'll be implementing something a little more straight-
forward. The server is going to be a card dealer - yes, a card dealer,
not a used car dealer. The server will be responsible for maintaining
a shoe of multiple decks of cards, and dealing the next card from the shoe
whenever a client requests one.
The Server
The usage for the program should be as follows:
card_dealer <num_decks> [<port_num>]
where num_decks is the number of decks to shuffle together into the
shoe, and port_num is the port on which the server will listen.
If port_num is omitted, the server should allow the OS to assign an
ephemeral port (i.e. choose it randomly).
You can assume that the user won't do something cruel and specify a
non-numeric string, a number <= 0, or some really big number for the number
of decks or the port number.
When the server starts, it should print the number of decks and the port number
it is using, initialize any data structures it needs to represent the shuffled
decks, then open a UDP server socket and go into a loop waiting for requests.
The server must support the following requests, submitted as simple ASCII text:
Each request should be terminated with a new-line ('\n') character.
The server should return the following responses:
- [02-9JQKA][CHSD] (for the DEAL request)
- OK (for the SHUFFLE request)
For those not familiar with regular expression notation, the deal response
should contain one character from the list of digits 2-9 and 0, or one of the
letters J, Q, K, or A. This represents the value of the card, 2-9, Ten, Jack,
Queen, King, or Ace. The response should then have one of the characters C, H,
S, or D (for Clubs, Hearts, Spades, or Diamonds).
The SHUFFLE command must cause the server to reinitialize the shoe with the
specified number of decks. If the client keeps submitting DEAL requests until
all the cards are dealt (52 times the number of decks), you should automatically
reshuffle.
Each time the server receives a request, it must print out the IP address and
port of the sender (the client), the request received, and the response it is
going to send. The responses must be formatted like this:
- Client 123.45.67.89, port 12345: dealt JH
- Client 123.45.67.90, port 2345: shuffled
Make sure you format the output this way, so that it is easy to pick out the
cards that the server dealt with a simple script.
One thing that will make this easier than the POP client is the use of the
UDP protocol. Each datagram will be a complete message, and you won't have
to be concerned about how a request might be split across multiple
recv() calls in a stream. Another thing that will be easier is that
you don't have to call accept() each time a client wants to talk to you,
so the server will only need to deal with one socket. Note that because you
are using UDP, you won't be able to call recv() and have it return -1 when
the client breaks the connection. You will need to use <CTRL>-C to break
out of the program when you are done running it.
The Client
In order to test the server, you'll also need to write a simple client program
that will request cards from the server. The usage for the client should be
card_client <host_name> <port_num>
where host_name can be either a domain name (i.e.
esus or esus.cs.montana.edu) or an IP address (153.90.199.47).
The client should have a simple menu:
- Request next card
- Shuffle deck
- Quit
When the client starts up, it should create a UDP client socket, then enter a
loop displaying the menu, getting user input, and executing the requested
command until the user selects "Quit". For each card request, the client
should print the card that was returned by the server.
Testing
If your server is working correctly, you should be able able to start it with
one deck, deal 53 cards, and make sure that for the first 52 it never returns
the same card twice (and it always returns a valid card). On the 53rd it will
have to start repeating. You should also be able to connect with two different
clients, have each of them request 27 cards, and see the same thing.
That's the reason for the specific formatting of the output. Try starting
your server with the following command:
card_server 40001 | tee tmp_outfile
The tee command takes a copy of whatever goes to the output and puts it
in the specified file too. It's similar to script, but you can't see
anything that is typed as input. Start a client, request at least 53 cards,
stop the client, then stop the server (with a <CTRL>-C). Then enter the
following:
grep dealt tmp_outfile | awk '{print $NF}' | sort | uniq -c
This should hopefully show you that each of the cards was only dealt once,
except for any after the 52nd, when the reshuffle occurs. Unless you requested
more than 104 cards, none of them should have a count greater than 2.
To just make sure of the number of different cards dealt, enter the same
command line, and add
This should return the number of unique lines - this should be 52.
Designing a Solution
Before you jump into an editor and start hacking on code, you really should sit
down and think about how you want to implement the program. The networking
part of it should be pretty straight-forward, but you'll need to think about
how you want to keep track of which cards have already been dealt and how you
will randomly choose the next card to deal from the remaining cards. As part
of the assignment, you will need to turn in your design documentation; it
doesn't need to be anything elaborate, but it should indicate how you are
planning to maintain the card data and list the socket calls that you will need
to use in the program. I can't make you do the design before you start coding,
but try it, you might like it.
Building a Code Library
You should also start trying to clean up and organize your code - as you have
probably noticed, a lot of the things you need to do in each program are pretty
repetitive. So you should start building some functions that you can reuse.
For this assignment, you don't need to split them out, but for the next one,
you will need to have a library, so start organizing the code into functions
now while you're working on this assignment.
For example, you will probably find it advantageous to have functions like the
following as the semester progresses:
- int open_tcp_srvr_sock( int port_num )
- int open_udp_srvr_sock( int port_num )
- int open_tcp_clnt_sock( const struct sockaddr * srvr_addr, int srvr_port )
- int open_udp_clnt_sock( const struct sockaddr * srvr_addr, int srvr_port )
- int get_service_port( const char * service_name, const char * protocol )
You don't need to use these names, or even group the functionality in the same
way. But you should be identifying common activities that you need to do that
take more than one line of code and are thus candidates for your library. (If
they only take one line of code, it's probably better to just call the standard
library function and not obscure it with some other name.)
Assignment Submission
NO hard copy, please. Everything should be submitted via email
by the due date - mail everything to Anthony. Follow Anthony's specifications
for assignment submissions. You need to include the design, your source, and
the output from your server for two different test runs. For the first test
run, start the server with one deck and choose one of the ports designated for
the lab (40000-40007). If your server can't open a socket on that port, try
another one; you should succeed before you exhaust all eight possibilities.
Start a client in another window and request twenty cards, shuffle, then
request five more cards. For the second run, start the server with four
decks, choosing the port as in the previous run, then start two different
clients and request twenty cards from each. Note that you don't have to
capture or turn in the client output, since the server output should include
all of the same pertinent information. You can just use tee to capture
the server runs, since there won't be any input on the server side.
You should include the following:
- Your name
- Anything that you thought was particularly interesting or problematic
in getting the program running and tested
- The program design
- The output of a run of the server with 1 deck
- The output of a run of the server with 4 decks
- The source for your program
The first two things should just be in the body of the email - attach the
design, source, and output as separate files.