简体   繁体   中英

Why does my Java client/server app hang when using ObjectInputStream but doesn't when I use BufferedReader with InputStreamReader?

I am wondering why it gets stuck on the following line, but it didn't used to get stuck when I was using a BufferedReader with an InputStreamReader:

input = new ObjectInputStream(socket.getInputStream());

Here is my client code:

import java.awt.BorderLayout;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;


public class MtgTestRunsClient {

    private JFrame frame = new JFrame("MTG Test Runs");

    private static int PORT = 8901;
    private Socket socket;
    //private BufferedReader inFromServer;
    //private PrintWriter outToServer;
    private ObjectInputStream inFromServer;
    private ObjectOutputStream outToServer;
    private Planeswalker planeswalker;

    public MtgTestRunsClient(String serverAddress) throws Exception {

        // Setup networking
        socket = new Socket(serverAddress, PORT);
        //inFromServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //outToServer = new PrintWriter(socket.getOutputStream(), true);
       inFromServer = new ObjectInputStream(socket.getInputStream());
       outToServer = new ObjectOutputStream(socket.getOutputStream());

        planeswalker = new BUGDelverPlaneswalker();
        planeswalker.setOutputToServer(outToServer);

        // Frame content
        JPanel contentPane = new JPanel(new BorderLayout());
        frame.setContentPane(contentPane);
        frame.getContentPane().add(planeswalker.getStatusBar(), BorderLayout.SOUTH);
        frame.getContentPane().add(planeswalker, BorderLayout.CENTER);
    }

    public void play() throws Exception {
        //String response;
        Object response;
        try {
            //response = inFromServer.readLine();
            response = inFromServer.readObject();
            if (response instanceof String ){
                if( ((String)response).startsWith("WELCOME")) {
                    char mark = ((String)response).charAt(8);
                    frame.setTitle("MTG Test Runs - Player " + mark);
                }
            }
            while (true) {
                //response = inFromServer.readLine();
                response = inFromServer.readObject();
                if (response instanceof String ){
                    if (((String)response).startsWith("OPPONENT_MOVED")) {
                        planeswalker.getStatusBar().setStatusString("Opponent "+((String)response).substring(15), false, true);
                    } else if (((String)response).startsWith("GAME_OVER")) {
                        break;
                    } else if (((String)response).startsWith("MESSAGE")) {
                        String messageText = ((String)response).substring(8);
                        planeswalker.getStatusBar().setStatusString(messageText, false, true);
                    }
                }else if(response instanceof Planeswalker){
                    planeswalker.setOpponent((Planeswalker)response);
                }
            }
            outToServer.writeObject("QUIT");
            outToServer.flush();
        }
        finally {
            socket.close();
        }
    }

    private boolean wantsToPlayAgain() {
        int response = JOptionPane.showConfirmDialog(frame,
            "Want to play again?",
            "Tic Tac Toe is Fun Fun Fun",
            JOptionPane.YES_NO_OPTION);
        frame.dispose();
        return response == JOptionPane.YES_OPTION;
    }

    /**
     * Runs the client as an application.
     */
    public static void main(String[] args) throws Exception {
        while (true) {
            String serverAddress = (args.length == 0) ? "localhost" : args[1];
            MtgTestRunsClient client = new MtgTestRunsClient(serverAddress);
            client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            client.frame.setExtendedState(client.frame.getExtendedState()|JFrame.MAXIMIZED_BOTH);
            client.frame.setVisible(true);
            client.frame.repaint();
            client.play();
            if (!client.wantsToPlayAgain()) {
                break;
            }
        }
    }
}

Here is my server code:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket;

/** * A server for a network multi-player tic tac toe game. Modified and * extended from the class presented in Deitel and Deitel "Java How to * Program" book. I made a bunch of enhancements and rewrote large sections * of the code. The main change is instead of passing data between the * client and server, I made a TTTP (tic tac toe protocol) which is totally * plain text, so you can test the game with Telnet (always a good idea.) * The strings that are sent in TTTP are: * * Client -> Server Server -> Client * ---------------- ---------------- * MOVE (0 <= n <= 8) WELCOME (char in {X, O}) * QUIT VALID_MOVE * OTHER_PLAYER_MOVED * VICTORY * DEFEAT * TIE * MESSAGE * * A second change is that it allows an unlimited number of pairs of * players to play. */ public class MtgTestRunsServer {

/**
 * Runs the application. Pairs up clients that connect.
 */
public static void main(String[] args) throws Exception {
    ServerSocket listener = new ServerSocket(8901);
    System.out.println("MTG Test Runs Server is Running");
    try {
        while (true) {
            Game game = new Game();
            Game.Player player1 = game.new Player(listener.accept(), '1');
            Game.Player player2 = game.new Player(listener.accept(), '2');
            player1.setOpponent(player2);
            player2.setOpponent(player1);
            game.currentPlayer = player1;
            player1.start();
            player2.start();
        }
    } finally {
        listener.close();
    }
}

}

/** * A two-player game. */ class Game {

Player currentPlayer;

public synchronized boolean legalMove(Player player, String move) {
    if (player == currentPlayer ) {
        currentPlayer = currentPlayer.opponent;
        currentPlayer.otherPlayerMoved(move);
        return true;
    }
    return false;
}

class Player extends Thread {
    char playerNo;
    Player opponent;
    Socket socket;
    //BufferedReader input;
    //PrintWriter output;
    ObjectInputStream input;
    ObjectOutputStream output;

    public Player(Socket socket, char playerNumber) {
        this.socket = socket;
        this.playerNo = playerNumber;
        try {
            //input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //output = new PrintWriter(socket.getOutputStream(), true);
            output = new ObjectOutputStream(socket.getOutputStream());
            output.writeObject("WELCOME " + playerNumber);
            output.flush();
            output.writeObject("MESSAGE Waiting for opponent to connect");
            output.flush();
            **input = new ObjectInputStream(socket.getInputStream());** // Must do this after constructed ObjectOutputStream above
            //output.println("WELCOME " + playerNumber);
        } catch (IOException e) {
            System.out.println("Player died: " + e);
        }
    }

    /**
     * Accepts notification of who the opponent is.
     */
    public void setOpponent(Player opponent) {
        this.opponent = opponent;
    }

    /**
     * Handles the otherPlayerMoved message.
     */
    public void otherPlayerMoved(String move) {
        //output.println("OPPONENT_MOVED " + move);
        try {
            output.writeObject("OPPONENT_MOVED " + move);
            output.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * The run method of this thread.
     */
    public void run() {
        try {
            // The thread is only started after everyone connects.
            //output.println("MESSAGE All players connected");
            output.writeObject("MESSAGE All players connected");
            output.flush();

            // Tell the first player that it is her turn.
            if (playerNo == '1') {
                //output.println("MESSAGE Your move");
                output.writeObject("MESSAGE Your move");
                output.flush();
            }

            // Repeatedly get commands from the client and process them.
            while (true) {
                //String command = input.readLine();
                Object command;
                try {
                    command = input.readObject();
                    if(command instanceof String){
                        if (((String)command).startsWith("MOVE")) {
                            String move = ((String)command).substring(5);
                            if (legalMove(this, move)) {
                                //output.println("VALID_MOVE");
                                output.writeObject("VALID_MOVE");
                            } else {
                                output.writeObject("MESSAGE INVALID_MOVE");
                                //output.println("MESSAGE INVALID_MOVE");
                            }
                        } else if (((String)command).startsWith("QUIT")) {
                            return;
                        }
                    }
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            System.out.println("Player died: " + e);
        } finally {
            try {socket.close();} catch (IOException e) {}
        }
    }
}

}

Straight from the javadoc :

Creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header.

You need to construct the a ObjectOutputStream before the ObjectInputStream. Otherwise you get a deadlock because of the stream header written and read by the constructors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM