Sample Screen-shots
See Client
TicTacToeServer.java
// Fig. 28.11: TicTacToeServer.java // Server side of client/server Tic-Tac-Toe program. import java.awt.BorderLayout; import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; import java.util.Formatter; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import javax.swing.JFrame; import javax.swing.JTextArea; import javax.swing.SwingUtilities; public class TicTacToeServer extends JFrame { private String[] board = new String[9]; // tic-tac-toe board private JTextArea outputArea; // for outputting moves private Player[] players; // array of Players private ServerSocket server; // server socket to connect with clients private int currentPlayer; // keeps track of player with current move private final static int PLAYER_X = 0; // constant for first player private final static int PLAYER_O = 1; // constant for second player private final static String[] MARKS = {"X", "O"}; // array of marks private ExecutorService runGame; // will run players private Lock gameLock; // to lock game for synchronization private Condition otherPlayerConnected; // to wait for other player private Condition otherPlayerTurn; // to wait for other player's turn // set up tic-tac-toe server and GUI that displays messages public TicTacToeServer() { super("Tic-Tac-Toe Server"); // set title of window // create ExecutorService with a thread for each player runGame = Executors.newFixedThreadPool(2); gameLock = new ReentrantLock(); // create lock for game // condition variable for both players being connected otherPlayerConnected = gameLock.newCondition(); // condition variable for the other player's turn otherPlayerTurn = gameLock.newCondition(); for (int i = 0; i < 9; i++) board[i] = new String(""); // create tic-tac-toe board players = new Player[2]; // create array of players currentPlayer = PLAYER_X; // set current player to first player try { server = new ServerSocket(12345, 2); // set up ServerSocket } catch (IOException ioException) { ioException.printStackTrace(); System.exit(1); } outputArea = new JTextArea(); // create JTextArea for output add(outputArea, BorderLayout.CENTER); outputArea.setText("Server awaiting connections\n"); setSize(300, 300); // set size of window setVisible(true); // show window } // wait for two connections so game can be played public void execute() { // wait for each client to connect for (int i = 0; i < players.length; i++) { try // wait for connection, create Player, start runnable { players[i] = new Player(server.accept(), i); runGame.execute(players[i]); // execute player runnable } catch (IOException ioException) { ioException.printStackTrace(); System.exit(1); } } gameLock.lock(); // lock game to signal player X's thread try { players[PLAYER_X].setSuspended(false); // resume player X otherPlayerConnected.signal(); // wake up player X's thread } finally { gameLock.unlock(); // unlock game after signalling player X } } // display message in outputArea private void displayMessage(final String messageToDisplay) { // display message from event-dispatch thread of execution SwingUtilities.invokeLater( new Runnable() { public void run() // updates outputArea { outputArea.append(messageToDisplay); // add message } } ); } // determine if move is valid public boolean validateAndMove(int location, int player) { // while not current player, must wait for turn while (player != currentPlayer) { gameLock.lock(); // lock game to wait for other player to go try { otherPlayerTurn.await(); // wait for player's turn } catch (InterruptedException exception) { exception.printStackTrace(); } finally { gameLock.unlock(); // unlock game after waiting } } // if location not occupied, make move if (!isOccupied(location)) { board[location] = MARKS[currentPlayer]; // set move on board currentPlayer = (currentPlayer + 1) % 2; // change player // let new current player know that move occurred players[currentPlayer].otherPlayerMoved(location); gameLock.lock(); // lock game to signal other player to go try { otherPlayerTurn.signal(); // signal other player to continue } finally { gameLock.unlock(); // unlock game after signaling } return true; // notify player that move was valid } else // move was not valid return false; // notify player that move was invalid } // determine whether location is occupied public boolean isOccupied(int location) { if (board[location].equals(MARKS[PLAYER_X]) || board [location].equals(MARKS[PLAYER_O])) return true; // location is occupied else return false; // location is not occupied } // place code in this method to determine whether game over public boolean isGameOver() { return false; // this is left as an exercise } // private inner class Player manages each Player as a runnable private class Player implements Runnable { private Socket connection; // connection to client private Scanner input; // input from client private Formatter output; // output to client private int playerNumber; // tracks which player this is private String mark; // mark for this player private boolean suspended = true; // whether thread is suspended // set up Player thread public Player(Socket socket, int number) { playerNumber = number; // store this player's number mark = MARKS[playerNumber]; // specify player's mark connection = socket; // store socket for client try // obtain streams from Socket { input = new Scanner(connection.getInputStream()); output = new Formatter(connection.getOutputStream()); } catch (IOException ioException) { ioException.printStackTrace(); System.exit(1); } } // send message that other player moved public void otherPlayerMoved(int location) { output.format("Opponent moved\n"); output.format("%d\n", location); // send location of move output.flush(); // flush output } // control thread's execution public void run() { // send client its mark (X or O), process messages from client try { displayMessage("Player " + mark + " connected\n"); output.format("%s\n", mark); // send player's mark output.flush(); // flush output // if player X, wait for another player to arrive if (playerNumber == PLAYER_X) { output.format("%s\n%s", "Player X connected", "Waiting for another player\n"); output.flush(); // flush output gameLock.lock(); // lock game to wait for second player try { while(suspended) { otherPlayerConnected.await(); // wait for player O } } catch (InterruptedException exception) { exception.printStackTrace(); } finally { gameLock.unlock(); // unlock game after second player } // send message that other player connected output.format("Other player connected. Your move.\n"); output.flush(); // flush output } else { output.format("Player O connected, please wait\n"); output.flush(); // flush output } // while game not over while (!isGameOver()) { int location = 0; // initialize move location if (input.hasNext()) location = input.nextInt(); // get move location // check for valid move if (validateAndMove(location, playerNumber)) { displayMessage("\nlocation: " + location); output.format("Valid move.\n"); // notify client output.flush(); // flush output } else // move was invalid { output.format("Invalid move, try again\n"); output.flush(); // flush output } } } finally { try { connection.close(); // close connection to client } catch (IOException ioException) { ioException.printStackTrace(); System.exit(1); } } } // set whether or not thread is suspended public void setSuspended(boolean status) { suspended = status; // set value of suspended } } public static void main(String[] args) { TicTacToeServer application = new TicTacToeServer(); application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); application.execute(); } }
Maintained by John Loomis, updated Sun Apr 09 17:08:54 2017