[英]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: 我想知道为什么它会卡在下面这一行,但是当我使用带有InputStreamReader的BufferedReader时它不会被卡住:
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.BufferedReader; import java.io.IOException; import java.io.IOException; import java.io.InputStreamReader; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.ServerSocket; import java.net.Socket; import java.net.Socket;
/** * A server for a network multi-player tic tac toe game. / ** *用于网络多玩家tic tac toe游戏的服务器。 Modified and * extended from the class presented in Deitel and Deitel "Java How to * Program" book. 修改和*扩展Deitel和Deitel“Java How to * Program”一书中提供的类。 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. 主要的变化是不是在*客户端和服务器之间传递数据 ,而是制作了一个完全*纯文本的TTTP(tic tac toe协议),所以你可以用Telnet测试游戏(总是一个好主意。)*字符串在TTTP中发送的是:* *客户端 - >服务器服务器 - >客户端* ---------------- ---------------- * MOVE(0 <= n <= 8)WELCOME({X,O}中的字符} * QUIT VALID_MOVE * OTHER_PLAYER_MOVED * VICTORY * DEFEAT * TIE * MESSAGE * *第二个变化是它允许无限数量的*对*玩家玩。 */ public class MtgTestRunsServer { * / 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 : 直接来自javadoc :
Creates an ObjectInputStream that reads from the specified InputStream. 创建一个从指定的InputStream读取的ObjectInputStream。 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. 此构造函数将阻塞,直到相应的ObjectOutputStream已写入并刷新标头。
You need to construct the a ObjectOutputStream before the ObjectInputStream. 您需要在ObjectInputStream 之前构造一个ObjectOutputStream。 Otherwise you get a deadlock because of the stream header written and read by the constructors. 否则,由于构造函数编写并读取了流标头,因此会出现死锁。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.