Making Java Tic Tac Toe Server Multithreaded

I have no experience with threads, unfortunately. I have been doing a couple of "Hello World" thread examples, which seem to be pretty easy. However, I need to incorporate threads into a Java Tic Tac Toe server that I have (which uses sockets). The design of my Tic Tac Toe game is this:

Provider.java (server): Determines the current player, sends textual display/game board to the current player, determines if game is over

Requester.java (client): Initiates a game board object for each client, keeps the clients game board in sync with the server's game board, checks for valid moves

TBoard.java: Methods used by both client and server (make_move(), is_winner(), current_player(), etc.)

I was wondering if you guys could take a look at my Provider.java (server), and give me some advice as to how to incorporate threads to allow for multiple tic tac toe games to be played simultaneously...

 /**
  * Provider.java
  * 
  * @version     2.0
  */
 import java.io.*;
 import java.net.*;

 /**
  * The Provider class is responsible for any server-side activities with the
  * Tic-Tac-Toe game. This includes:
  * 
  * 1. Determining the current player 2. Managing the textual display for the two
  * individual players 3. Updating the game board with the each player's moves 4.
  * Determine what player wins, if any
  */
 public class Provider {

TBoard board = new TBoard(); // Instantiate the game board object

ServerSocket providerSocket;
Socket connection1 = null, connection2 = null;
ObjectOutputStream out, out2; // Client 1, Client 2
ObjectInputStream in, in2; // Client 1, Client 2
String message;
Boolean done = false;
String end = "";
int row, col, game_state = 3;

/**
 * Class constructor.
 */
Provider() {
}

void run() {
    try {
        // Open a socket and wait for connections
        providerSocket = new ServerSocket(20092);

        System.out.println("Waiting for connection...");
        connection1 = providerSocket.accept();
        System.out.println("Connection received from Player 1 "
                + connection1.getInetAddress().getHostName());
        connection2 = providerSocket.accept();
        System.out.println("Connection received from Player 2 "
                + connection2.getInetAddress().getHostName());

        out = new ObjectOutputStream(connection1.getOutputStream());
        out2 = new ObjectOutputStream(connection2.getOutputStream());

        in = new ObjectInputStream(connection1.getInputStream());
        in2 = new ObjectInputStream(connection2.getInputStream());

        do {
            // Send the game game_state to the current player
            sendInt(board.get_player(), game_state);

            // If the game state != 3 (There is a winner or board is full)
            if (game_state != 3) {
                // Send game state to the other player
                sendInt(board.get_opposite_player(), game_state);

                done = true; // Condition to terminate the server
                // If game is in "play" state
            } else {
                // Send both the current player and the Tic Tac Toe board
                // (2D array) to current player
                sendInt(board.get_player(), board.get_player());
                send2D(board.get_player(), board.print_board());

                sendString(board.get_player(),
                        "Please enter a row, press Enter, then enter a column: ");

                // Receive the tic tac toe board from current player
                // (after a move has been made)
                if (board.get_player() == 1) {
                    int[][] c_array = (int[][]) in.readObject();
                    board.set_array(c_array);
                } else {
                    int[][] c_array = (int[][]) in2.readObject();
                    board.set_array(c_array);
                }

                // Switch the current player
                if (board.get_player() == 1) {
                    board.set_player(2);
                } else {
                    board.set_player(1);
                }

                // If there is a winner, set the game state accordingly
                if (board.winner() != 0) {

                    if (board.get_player() == 1) {
                        game_state = 2;
                    } else {
                        game_state = 1;
                    }

                    // If there is no winner and the board is full, set the
                    // game state accordingly
                } else if (board.board_full() && board.winner() == 0) {

                    game_state = 0;

                    // Otherwise, stay in the "play" state
                } else {

                    game_state = 3;

                }
            }

        } while (done != true);

    } catch (IOException ioException) {
        ioException.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        // Close the input/output streams, and the socket connections
        try {

            in.close();
            out.close();
            in2.close();
            out2.close();
            providerSocket.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

/**
 * Sends a String to the current client
 * 
 * @param player the current player
 * @param msg the String to be sent
 */
void sendString(int player, String msg) {
    try {
        if (player == 1) {
            out.writeObject(msg);
            out.flush();

        } else {
            out2.writeObject(msg);
            out2.flush();
        }
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }
}

/**
 * Sends a String to the current client
 * 
 * @param player the current player
 * @param array the 2D array to be sent
 */
void send2D(int player, int[][] array) {
    try {
        if (player == 1) {
            out.writeObject(array);
            out.flush();

        } else {
            out2.writeObject(array);
            out2.flush();
        }
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }
}

/**
 * Sends a int to the current client
 * 
 * @param player the current player
 * @param msg the int to be sent
 */
void sendInt(int player, int msg) {
    try {
        if (player == 1) {
            out.writeObject(msg);
            out.flush();

        } else {
            out2.writeObject(msg);
            out2.flush();
        }
    } catch (IOException ioException) {
        ioException.printStackTrace();
    }
}
/**
 * Main method, invoking run() method
 */
public static void main(String args[]) {
    Provider server = new Provider();
    server.run();
}
 }

Thanks!

13.10.2009 20:01:04
2 ОТВЕТА
РЕШЕНИЕ

First, completely separate the actual Tic-Tac-Toe stuff from your communication layer. Your communication layer should basically receive a message from any client, figure out which Tic-Tac-Toe instance belongs to this client, then dispatch the message. Similarly, the Tic-Tac-Toe instance might need to send a message to its players through the communication layer.

I imagine that your TicTacToe class will have a very simple API that looks like:

public Result mark(int row, int col, int playerID);

Where Result can either be something like VALID, INVALID_MOVE, PLAYER_WINS, or DRAW, or something like that. With an interface like this, you can easily create a single-player version of the game before you move on to the networking stuff.

In the end, you'll need 1 thread which exclusively calls serverSocket.await(), waits for 2 incoming connections, then creates a new Tic-Tac-Toe instance. Anytime a message comes from either of these 2 clients, you'll dispatch it to that particular instance. You need some way to lookup the TicTacToe instance for the given socket, extract the message from the input stream, modify the TicTacToe instance, then send a message back to the 2 player clients.

You'll also need an additional thread for each individual socket connection. Everytime you try to read from a socket's input stream, you'll need to wait for some data to actually be sent from the client. This is where the threads come into play.

Oh, by the way, the java.util.concurrent package provides ways to make your network server more scalable and efficient but its pretty complicated. Also, most ways of using it are actually single-threaded (which is actually why it's more scalable, believe it or not). I recommend steering clear for this assignment.

1
13.10.2009 20:22:54
Thanks for the advice, I will try to separate the communication stuff from the Tic Tac Toe stuff.
littleK 13.10.2009 20:26:30

Check out the java.util.concurrent package.

1
13.10.2009 20:04:48