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 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:

  1. a total of 4 players have been accepted (initial + 3 more)
    - OR -
  2. 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:

Implementation Guidelines

In clarifying the "restricted rules" of the game, you are aiming for the following:

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.

  1. 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.
  2. 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



Last Modified: 2012 / 03 / 22