Operating Systems
CIS*3110 (W12)
Assignment #3
Due: April 5, 2012 at 23:55h
(please see deliverables section below)
Key concepts: Distributed programming; client-server computing;
implementing to a protocol.
Read the following sections carefully to ensure that you know what is to be
done and what is to be handed in. If the details aren't addressed
successfully you will lose marks. Please refer to the
coding style guidelines to provide
direction regarding the format of the code you write. You are expected to
work independently on this assignment (please see
academic misconduct in computing
for clarification if necessary).
Blackjack!
Servers are persistent programs that provide services to other programs in a
networked environment. Clients are programs that request services from server
programs over a network. By way of example, your interaction with the web is
entirely the result of a client-server computing model, where the web browser
you are running is the client, requesting services (pages) from remote web
servers which exist purely to service these client requests. To better
understand distributed programming, you will write a TCP-based server
that acts as a dealer for the card game Blackjack.
The Problem
Using the C programming language, you are to implement a server capable of
acting as a dealer for the card game Blackjack, which operates over the
Internet using TCP protocols. For the purposes of this assignment we will use
a simplified version of the rules for the game
(see Wikipedia Blackjack
entry; the rules we will use are documented in "Rules of play at
casinos" with the following restrictions: player decisions are restricted to
hit and stand, there is no notion of more sophisticated behaviour
such as "doubling down", "splitting", "insurance" or even the notion of a
"blackjack" being a significant hand --- this is a very vanilla flavour of
the game). These rules are to be realized in the server (and dealer) with
a specific protocol (see below) which will allow any client processes which
also implement the protocol to connect to your server and play the game.
Criteria that your server should satisfy include:
- The server should be accessible from both local and remote sites. This
means that the dealer and the player processes may reside on different
machines, provided that these machines are connected by the Internet.
- Use of virtual circuit communication channels in the form of
internet domain sockets (domain type: AF_INET, socket
type: SOCK_STREAM---these concepts will be covered in more
detail in the seminars).
- The master socket to which clients will connect should use port number
2000 + the last three digits of your student ID. For example, if you
student ID is 0123456, you should be using port number 2456. This
port number should appear in the README file that accompanies your
submission.
- Busy-waiting must be avoided.
- The server must support the protocol outlined below.
The Protocol
The game is played in "rounds" in which a number of player connections are
accepted by the server, and a hand of blackjack played with each in
accordance with the rules provided (see link and restrictions above).
The server initiates a round by accepting the first available connection from
its master socket. Player processes connect to the server to express their
willingness to participate in the current round.
When a player first connects, it sends the server the passcode
0xfacebeef (note that this is effectively a 32-bit integer, just
written in hex here as it is amusing --- ensure you take into consideration
network byte order when transmitting this value). This value indicates to the
server that this is an actual player that knows the rules of the game (and
uses the correct protocol). A player that doesn't send this value within 5
seconds should be disconnected (its socket closed) and ignored. Once the
first player is authenticated, the server should proceed to accept additional
players in the same fashion until:
- a total of 4 players have been accepted (initial + 3 more)
- OR -
- 30 seconds have elapsed
at which point the "table is closed" and the round may commence with the
available player connections.
Once a set of players have been accepted as outlined above, the server should
fork off a child process, which executes the dealer process which will play
the game with the connected players (note that the dealer process is to be a
separate program - so the main server forks a child which calls exec to
become the dealer process). The server should wait for the dealer to finish
the round (i.e. child process terminates) and the process repeats itself.
Note that the master socket is never closed.
The dealer begins by sending each player their cards, followed by the single
visible dealer card (see format below). As soon as a player receives this
information they may begin playing their hand in accordance with the rules.
Specifically, a player may send the message HIT, to which the dealer
will respond with another card (same format), or STAND, to indicate
they are satisfied with their hand. If a "HIT" results in the player going
bust, which the dealer is aware of as it can keep track of the player's hand,
the dealer disconnects the appropriate player connection after "dealing"
(sending) the card---the dealer automatically wins that hand with that player.
Important: the dealer must process player's requests as soon as they
are received; the dealer will have to monitor all connections for data
simultaneously rather than process them in a round-robin fashion.
Once all players have either chosen to stand or gone bust, the dealer plays
the hand it is holding for each remaining connected player according to the
criteria specified in the restricted rule-set we are using for this exercise.
Each card the dealer draws is sent to each remaining player in turn (as noted
above). Once the dealer either stands or goes bust, the respective player
connection is terminated and the dealer moves on to the next player, if any.
Note: since the player knows all cards played, it is capable of
determining the outcome of the game for itself.
Once the dealer has completed a hand with all players, or all connections have
been terminated in any case, it exits.
Players keep track of their own bets and do their own book-keeping with
respect to winnings/losings (if applicable---it is not necessary to
implement this using gambling conventions; as the server does not track such
information, any implementation of betting or money occurs entirely in the
client which is something you've made purely for your own use; we will use
our client to test your server on submission).
Format of cards and messages
Although it doesn't matter how you do book-keeping within the server generally,
it is necessary to have standards for the format in which this information is
communicated to clients.
"Cards" are represented by a sequence of 2 char's, the first is the
ASCII value of the card (2, 3, 4, 5, 6, 7, 8, 9, T, J, Q, K, A - note that we
define "10" to be "T" to allow every card to be represented by a single
character), the second is the ASCII value of the suit (S, H, D, C). For
example, player's cards: seven of hearts, three of clubs together with the
dealer's visible card the ace of diamonds would appear as the stream of
characters: 7H3CAD.
"Messages" are passed as ASCII text, terminated by a nul byte ('\0').
Caveat
The dealer should operate correctly in the face of possibly misbehaving
players. In particular, if a player terminates unexpectedly or sends a
message that doesn't conform to protocol rules, it should be disconnected.
You may assume that a player is terminating if it doesn't respond with its
STAND or HIT messages within 10 seconds (except after
the player "stands", of course). This 10 second limit should be applied to
each connected player individually.
Example
An example of an exchange between a player and the server/dealer for a single
hand (from the player's perspective) might go something like this:
- connect to server - sends "0xfacebeef"
- receive 7H4DAH
my hand: 7-hearts + 4-diamonds (value of hand = 11), dealer has
ace-hearts showing
- send: "HIT"
- receive: 9S
now have: 7-hearts + 4-diamonds + 9-spades (value of hand = 20)
- send: "STAND"
- receive: 3c4H, further read indicates EOF (i.e. connection
was terminated)
dealer had A-hearts, 3-clubs, 4-hearts and (as per the rules) stood
on 18 - I win!!
Implementation Guidelines
In clarifying the "restricted rules" of the game, you are aiming for the
following:
- play commences with 2 cards being dealt to every player "face down"
(only known to the player), and 2 cards dealt to the dealer, 1 of them
being face up (so everyone can see one of the dealer's cards, but not
the other); note that for this assignment it doesn't matter if the dealer
was actually given 2 cards initially, or if you just deal 1 for the
dealer and finish the second card when the dealer starts playing their
hand...the effect is the same given the restricted rules we're using.
- object of the game is to build a hand whose value is as close to 21 as
possible, without going over (all cards are worth their numeric face
value, except "face" cards (J,Q,K) which are worth 10, and Ace (A) which
is either worth 1 or 11 - as suits the player (the dealer can simply
always assume an Ace is worth the largest value possible that keeps a
legal hand (e.g. AH 4D = 15, AH 4D TC = 15).
- play rules for the dealer are entirely algorithmic (dealer behaviour is
entirely deterministic): if dealer hand is worth less than 17, dealer
HITs, if dealer hand is worth 17 or more, it stands; dealer will always
take the Ace as being valued 11 where possible---one special case is
A+6 (a so-called "soft 17"): dealer should HIT on a soft 17 in this
implementation.
- ties should be interpreted as a win for the dealer (in other words, if
the dealer has 21, it isn't possible for a player to win --- note that
there is no special processing related to this; the game plays out
normally but in the case of a tie with a player, it is considered a
win for the dealer.
- the dealer process is required to be making use of a simulated deck of
cards, however that behaviour is achieved --- a standard deck of cards
has 52 individual cards, one for each of the face values: A, 2, 3, 4,
5, 6, 7, 8, 9, T, J, Q, K, for each of the suits Spades, Hearts,
Diamonds and Clubs: 13 cards in each of 4 suits = 52 cards total; bottom
line you aren't just generating random numbers, but are consuming specific
valued cards from a deck of 52 in total; a "fresh deck" should be used
for every game.
The dealer process is required to be a separate program from the main
server. You will need to design a method by which you communicate the
open FDs that contain client connections to the dealer process given
the exec call that will load it over the forked child of the
server itself.
In the interest of clarity - please note that once a game has started, the
dealer interacts with individual clients in whatever order they individually
perform actions until all connected players indicate that they "STAND"
(or go BUST and are disconnected); once all players have finished their play
in this manner, the game is resolved independently for each connected player
in a round-robin fashion as the dealer plays out their hand with each player
separately (i.e. in our game, it is not possible for players to see other
player's cards, although they are all playing from the same deck).
Note that the communication protocol is completely defined for this
assignment and you must implement it correctly and not deviate from it,
as we will test your servers with our own client that uses the same protocol.
In fact, it should be possible to use arbitrary clients with arbitrary
servers, if correctly implemented. You should be seeking out other students
and arranging to test your clients against their servers (and vice versa) to
ensure you have implemented things appropriately. We will not use your
client to talk to your server for grading purposes.
You may use low-level I/O operations as appropriate; however, you should feel
free to use fdopen(3) to attach steams to a socket if it suits you.
Ensure you understand what you are doing, regardless of how you chose to
implement it. The protocol must be correct either way.
Child processes
Note that the operation of the server involves spawning child processes. As
you may recall from A2, a child process wants to report its exit status to
its parent, and will hang around in a "zombie" state until it the exit status
is consumed. Note that a child process sends its parent a SIGCHLD
signal when it is ready to exit.
You will need to have your server set up a signal handler to catch
SIGCHLD, and execute a wait(2) call in order for the child
to exit properly. This isn't complicated, and in fact is provided as an
example in the Labs related to A2. Just be aware that this signal can
interrupt blocked system calls (such as select(2)), which cannot
be restarted so will appear to return failure. You can access the system-
provided errno variable and check what the error is: if it is
EINTR it was simply an interrupted system call and should simply
be restarted. Otherwise it's a real error and should be handled as is
appropriate.
Warning: always clean up after yourself!!!
This assignment involves you explicitly using the fork() system call
to create new processes, and development and testing will involve experiments
with possibly many CPU-bound jobs running in the background.
- If you make mistakes with the use of fork() it is quite possible
to suck the machine on which you are running down to nothing with an
exploding number of processes (the aptly-named "fork-bomb"). You are
not to run this code on department equipment other than the REYN 001A
lab machines which are provided for this purpose. You can run on your
own computers if you like, just be aware that if you really hose them
you may need to reboot.
- Please make sure no jobs are left behind (intentionally or by accident)
when you leave a workstation---this could easily affect the next person
using that machine. Note that if during our testing of your program,
processes are improperly left running, your mark will be reduced by 10%
for each such process!
Makefile
You are responsible for writing a Makefile to compile your code. Typing
"make", "make blackjack" or "make all" should
result in the compilation of all required components with the result being your
blackjack server and subordinate dealer programs (blackjackd and
dealer). You should ensure that all required dependencies are
specified correctly.
Deliverables
- Source code:
- All source code required to compile and execute your
blackjackd server and dealer program. You are
encouraged to organized your code in a modular fashion so obviously
header files or other source code files required for your
implementation should be included.
- Makefile
- The required README file containing descriptions of the
files you are submitting and any notes that may be relevant to
the marker (e.g. compilation and execution instructions, etc.)
- Do NOT submit clients you may have developed for the purpose of
testing your server
- Documentation:
- Brief documentation should include:
- Statement of the problem solved
- Design notes: how you solved the problem (specifically, a
brief description of the solution you developed to solve the
problem).
- Decisions/assumptions/justifications: state any assumptions
you made in solving the problem and known limitations of
your implementation; justify the design decisions you made in
the previous section (i.e. why a particular approach and not
another?).
- Testing: indicate what methods you have used to ensure the
correct/appropriate operation of your solution.
- Documentation can be provided in PDF or text
format, and only those formats will be accepted. Do not submit
Word documents or any other random things you feel like. The
file should be named A3_docs.pdf or A3_docs.txt
as appropriate.
Electronic submission:
- All of the above, including the documentation and required README
file should be tar'ed, gzip'ed and submitted through the
Moodle system
as described in the
submission guidelines
for the course.
Last Modified: 2012 / 03 / 22