public void handleRequest( Socket replySocket, byte[] requestBytes ) throws IOException {
    String request = new String( requestBytes );
    StringTokenizer st = new StringTokenizer( request );
    String command = st.nextToken();
    if (command.equalsIgnoreCase( "load" ) ) {
        String fileName = st.nextToken();
        Card card = Track.loadCard(fileName);
        downloadCard( replySocket, card );
    } else if (command.equalsIgnoreCase( "download" ) ) {
        String trackName = st.nextToken();
        Card card = Track.getTrack( trackName ).getCurrentCard();
        downloadCard( replySocket, card );
    } else if (command.equalsIgnoreCase( "bet" ) ) {
        String trackName = st.nextToken();
        Track track = Track.getTrack( trackName );
        String poolName = st.nextToken();
        String horseNum = st.nextToken();
        BettingPool pool = track.getPool( track.getCurrentCard(), poolName );
        String amount = st.nextToken();
        pool.addBet( Integer.parseInt( horseNum )-1, Double.parseDouble( amount ) );
        if (pool.isWinPool() && track.currentRace() == pool.getRace() &&
                ((((new Date().getTime() - _lastUpdate.getTime()) / 1000) >= 20) && track.hasEnoughBets())) {
            newFrame( Constants.FMT_ODDS );
            sendData( 0, 0, trackName );
            sendData( 0, 1, Integer.toString( track.currentRace() ) );
            Race race = track.getCurrentCard().getRace( track.currentRace() );
            for (int i = 1; i <= race.getNumHorses(); i++) {
                sendData( i, 0, Integer.toString( i ) );
                sendData( i, 1, withNBSP( race.getHorseName( i ) ) );
                double otherBets = track.getPayoutPortion() * (pool.getTotalBets() - pool.getBet( i-1 ));
                double odds = Math.min( 100.0, otherBets / Math.max( 2, pool.getBet( i-1 ) ) );
                Fraction fraction = Fraction.toFraction( odds, 0.05, 4 );
                sendData( i, 2, fraction.getNumerator() + "-" + fraction.getDenominator() );
            }
        endFrame();
        }
    } else if (command.equalsIgnoreCase( "display" ) ) {
        String trackName = st.nextToken();
        Track track = Track.getTrack( trackName );
        int raceNum = Integer.parseInt( st.nextToken() );
        track.setCurrentRace( raceNum );
        _lastUpdate = new Date();
        Race race = track.getCurrentCard().getRace( raceNum );
        newFrame( Constants.FMT_ODDS );
        sendData( 0, 0, trackName );
        sendData( 0, 1, Integer.toString( raceNum ) );
        for (int i = 1; i <= race.getNumHorses(); i++) {
            sendData( i, 0, Integer.toString( i ) );
            sendData( i, 1, withNBSP( race.getHorseName( i ) ) );
            Fraction fraction = Fraction.toFraction( race.getInitialOdds( i ) );
            sendData( i, 2, fraction.getNumerator() + "-" + fraction.getDenominator() );
        }
        endFrame();
    } else if (command.equalsIgnoreCase( "results" ) ) {
        String trackName = st.nextToken();
        Track track = Track.getTrack( trackName );
        int raceNum = Integer.parseInt( st.nextToken() );
        Race race = track.getCurrentCard().getRace( raceNum );
        newFrame( Constants.FMT_RESULTS );
        sendData( 0, 0, trackName );
        sendData( 0, 1, Integer.toString( raceNum ) );
        int win   = Integer.parseInt( st.nextToken() );
        int place = Integer.parseInt( st.nextToken() );
        int show  = Integer.parseInt( st.nextToken() );
        sendData( 1, 0, Integer.toString( win ) );
        sendData( 1, 1, withNBSP( race.getHorseName( win ) ) );
        sendData( 2, 0, Integer.toString( place ) );
        sendData( 2, 1, withNBSP( race.getHorseName( place ) ) );
        sendData( 3, 0, Integer.toString( show ) );
        sendData( 3, 1, withNBSP( race.getHorseName( show ) ) );
        DecimalFormat format = new DecimalFormat( "#,##0.00" );
        BettingPool winPool = track.getPool( track.getCurrentCard(), "WIN-" + raceNum );
        double winExcess = track.getPayoutPortion() * (winPool.getTotalBets() - winPool.getBet( win-1 ));
        double winPayout = 2 * (winExcess + winPool.getBet( win-1 )) / winPool.getBet( win-1 );
        sendData( 1, 2, format.format( winPayout ) );
        BettingPool placePool = track.getPool( track.getCurrentCard(), "PLACE-" + raceNum );
        double placeExcess = track.getPayoutPortion() * (placePool.getTotalBets() - placePool.getBet( win-1 ) - placePool.getBet( place-1 )) / 2;
        double placePayout1 = 2 * (placeExcess + placePool.getBet( win-1 )) / placePool.getBet( win-1 );
        double placePayout2 = 2 * (placeExcess + placePool.getBet( place-1 )) / placePool.getBet( place-1 );
        sendData( 1, 3, format.format( placePayout1 ) );
        sendData( 2, 3, format.format( placePayout2 ) );
        BettingPool showPool = track.getPool( track.getCurrentCard(), "SHOW-" + raceNum );
        double showExcess = track.getPayoutPortion() * (showPool.getTotalBets() - showPool.getBet( win-1 ) - showPool.getBet( place-1 ) - showPool.getBet( show-1 )) / 3;
        double showPayout1 = 2 * (showExcess + showPool.getBet( win-1 )) / showPool.getBet( win-1 );
        double showPayout2 = 2 * (showExcess + showPool.getBet( place-1 )) / showPool.getBet( place-1 );
        double showPayout3 = 2 * (showExcess + showPool.getBet( show-1 )) / showPool.getBet( show-1 );
        sendData( 1, 4, format.format( showPayout1 ) );
        sendData( 2, 4, format.format( showPayout2 ) );
        sendData( 3, 4, format.format( showPayout3 ) );
        endFrame();
    }
}