尝试制作客户端服务器程序,特别是一个刽子手游戏。
我有4个文件:KnockKnockServer
,KnockKnockProtocol
,Hangman
和KnockKnockClient.
KnockKnockServer
运行,KnockKnockClient
连接到它。 KnockKnockProtocol
处理来自客户端的请求。如果KnockKnockProtocol
识别某个单词或短语,客户端会向用户发送请求,以便为特定单词提供答案。
Hangman文件是一个完整的Hangman
游戏(不是客户端服务器),我想重新使用它的一些方法,比如打印Hangman dude。把那个刽子手的家伙带到了客户端。
所以我使用字符串构建器来修改字符串。但我得到了奇怪的结果。 我应该使用字符串构建器还是其他东西? 我还需要修复什么,以便客户端可以接收Hangman资源? 我应该重新设计整个项目吗?
以下是服务器的打印方式:
run:
____
| |
|
|
|
|
|
|
__|__
_ _ _
Wrong letters: ____
| |
|
|
|
|
|
|
__|__
_ _ _
Wrong letters:
s
a
BUILD SUCCESSFUL (total time: 1 minute 31 seconds)
奇怪的是,它打印出两次Hangman花花公子。
以下是客户端显示的内容:
run:
Server: Connection Established.. Start hangman? (y/n)
y
Client: y
Server: ____
a
Client: a
Server: | |
e
Client: e
Server: |
i
Client: i
Server: |
o
Client: o
Server: |
s
Client: s
Server: |
r
Client: r
Server: |
r
Client: r
Server: |
r
Client: r
Server: __|__
s
Client: s
Server:
g
Client: g
Server: _ _ _
g
Client: g
Server: Wrong letters:
h
Client: h
Server: You're supposed to say "Little Old Lady who?"! Try again. Knock! Knock!
BUILD STOPPED (total time: 1 minute 15 seconds)
使用字符串构建器从服务器端重建字符串的客户端绘制一个刽子手。但它没有被正确绘制。
KnockKnockServer服务器端:
import java.net.*;
import java.io.*;
public class KnockKnockServer {
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: java EchoServer <port number>");
System.exit(1);
}
//sets portNumber to argument run value
int portNumber = Integer.parseInt(args[0]);
try (
ServerSocket serverSocket = new ServerSocket(portNumber);
Socket clientSocket = serverSocket.accept();
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
) {
String inputLine, outputLine;
KnockKnockProtocol kkp = new KnockKnockProtocol();
outputLine = kkp.processInput(null);
out.println(outputLine);
while ((inputLine = in.readLine()) != null) {
outputLine = kkp.processInput(inputLine);
out.println(outputLine);
if (outputLine.equals("Bye."))
break;
}
} catch (IOException e) {
System.out.println("Exception caught when trying to listen on port "
+ portNumber + " or listening for a connection");
System.out.println(e.getMessage());
}
}
}
KnockKnockProtocol服务器端:
import java.net.*;
import java.io.*;
public class KnockKnockProtocol {
private static final int WAITING = 0;
private static final int SENTKNOCKKNOCK = 1;
private static final int SENTCLUE = 2;
private static final int ANOTHER = 3;
private static final int NUMJOKES = 5;
private int state = WAITING;
private int currentJoke = 1;
private int counter = 0;
private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" };
private String[] answers = { "Turnip the heat, it's cold in here!",
"I didn't know you could yodel!",
"Bless you!",
"Is there an owl in here?",
"Is there an echo in here?" };
public String processInput(String theInput) {
Hangman hman = new Hangman();
String theOutput = null;
if (state == WAITING) {
theOutput = "Connection Established.. Start hangman? (y/n)";
state = SENTKNOCKKNOCK;
} else if (state == SENTKNOCKKNOCK) {
//if (theInput.equalsIgnoreCase("Who's there?")) {
if (theInput.equalsIgnoreCase("y")) {
//theOutput = clues[currentJoke];
theOutput = hman.printCurrentState();
System.out.println(theOutput);
state = SENTCLUE;
} else {
theOutput = "You guessed a wrong word" + " failed attempts: " + counter++ +
" Try agian. Start hangman? (y/n)";
}
} else if (state == SENTCLUE) {
if (theInput.equalsIgnoreCase(clues[currentJoke])) {
theOutput = answers[currentJoke] + " Play again? (y/n)";
state = ANOTHER;
} else {
theOutput = "You're supposed to say \"" +
clues[currentJoke] +
" who?\"" +
"! Try again. Knock! Knock!";
state = SENTKNOCKKNOCK;
}
} else if (state == ANOTHER) {
if (theInput.equalsIgnoreCase("y")) {
theOutput = "Guess a word";
if (currentJoke == (NUMJOKES - 1))
currentJoke = 0;
else
currentJoke++;
state = SENTKNOCKKNOCK;
} else {
theOutput = "Bye.";
state = WAITING;
}
}
return theOutput;
}
}
Hangman游戏服务器端:
///////////////////////////////////////////////////////////////////////////////
// Title: Hangman
// Files: Hangman.java
//////////////////////////// 80 columns wide //////////////////////////////////
import java.util.*;
/**
* This program implements the word guessing game called Hangman.
*
* <p>Bugs: none known
*
* @author CS302, 2009,2012 modified by Jim Skrentny
*/
public class Hangman {
//////////////////////////////////////////////////////////////////////
// 1. CLASS VARIABLE
//////////////////////////////////////////////////////////////////////
private static String [] words = //choose secret word from these
{"geography", "cat", "yesterday", "java", "truck", "opportunity",
"fish", "token", "transportation", "bottom", "apple", "cake",
"remote", "pocket", "terminology", "arm", "cranberry", "tool",
"caterpillar", "spoon", "watermelon", "laptop", "toe", "toad",
"fundamental", "capitol", "garbage", "anticipate", "apple"};
//////////////////////////////////////////////////////////////////////
// 2. INSTANCE VARIABLES
//////////////////////////////////////////////////////////////////////
private String secretWord; // the chosen secret word
private ArrayList<Character> correctLetters; // correct guesses
private ArrayList<Character> incorrectLetters; // incorrect guesses
private Scanner stdin = new Scanner(System.in); // for user input
//////////////////////////////////////////////////////////////////////
// 3. CONSTRUCTOR
//////////////////////////////////////////////////////////////////////
/**
* Constructs a Hangman game.
*/
public Hangman() {
//REMOVE LINE BELOW WHEN DONE TESTING
//this.secretWord = "miscellaneous";
//Randomly choose a word from list of words
//UNCOMMENT LINES BELOW TO PLAY WHEN DONE TESTING
Random randIndex = new Random();
int index = randIndex.nextInt(Hangman.words.length);
this.secretWord = Hangman.words[index];
this.correctLetters = new ArrayList<Character>();
for (int i = 0; i < this.secretWord.length(); i++)
this.correctLetters.add('_');
this.incorrectLetters = new ArrayList<Character>();
}
//////////////////////////////////////////////////////////////////////
// 4. PUBLIC INSTANCE METHODS
//////////////////////////////////////////////////////////////////////
/**
* playGame
*
* Play one game of Hangman until
* the user wins (guesses all of the letters in the secret word)
* or loses (guesses 7 incorrect letters):
*/
public void playGame() {
while (!gameOver()) {
//Print the Hangman picture
printHangman();
//Print the correct guesses in the secret word
for (int i = 0; i < this.correctLetters.size(); i++)
System.out.print(this.correctLetters.get(i) + " ");
//Print the incorrect letters that have been guessed
System.out.print("\nWrong letters: ");
for (int i = 0; i < this.incorrectLetters.size(); i++)
System.out.print(this.incorrectLetters.get(i) + " ");
//Prompt and read the next guess
System.out.print("\nEnter a lower-case letter as your guess: ");
String guess = stdin.nextLine();
//Process the next guess
handleGuess(guess.charAt(0));
}
System.out.println("The secret word was: " + secretWord);
if (gameWon()) {
System.out.println("Congratulations, you won!");
} else {
System.out.println("Sorry, you lost.");
printHangman();
}
}
/*
-
*/
public String printCurrentState() {
//Print the Hangman picture
StringBuilder sb = new StringBuilder();
sb.append(printHangman());
//Print the correct guesses in the secret word
for (int i = 0; i < this.correctLetters.size(); i++){
System.out.print(this.correctLetters.get(i) + " ");
sb.append(this.correctLetters.get(i) + " ");}
//Print the incorrect letters that have been guessed
System.out.print("\nWrong letters: ");
sb.append("\nWrong letters: ");
for (int i = 0; i < this.incorrectLetters.size(); i++){
System.out.print(this.incorrectLetters.get(i) + " ");
sb.append(this.incorrectLetters.get(i) + " ");}
return sb.toString();
}
//////////////////////////////////////////////////////////////////////
// 5. PRIVATE INSTANCE METHODS (HELPERS)
//////////////////////////////////////////////////////////////////////
/**
* handleGuess
*
* If the guessed letter (parameter ch) is in the secret word
* then add it to the array list of correct guesses and tell the user
* that the guess was correct;
* otherwise, add the guessed letter to the array list of incorrect
* guesses and tell the user that the guess was wrong.
*
* @param ch the guessed letter
*/
private void handleGuess(char ch) {
boolean chInSecretWord = false;
// go through the secret word character by character
for (int i = 0; i < this.secretWord.length(); i++) {
if (this.secretWord.charAt(i) == ch) { // if ch matches
chInSecretWord = true; // the guess was correct
this.correctLetters.set(i, ch); // update the user's guess
}
}
if (chInSecretWord)
System.out.println("The letter you guessed was correct!");
else { // the character was not in the secret word
this.incorrectLetters.add(ch);
System.out.println("Sorry, that letter is not in the secret word");
}
/////////////////////////
// TODO FILL IN CODE HERE
/////////////////////////
}
/**
* gameWon
*
* Return true if the user has won the game;
* otherwise, return false.
*
* @return true if the user has won, false otherwise
*/
private boolean gameWon() {
boolean won = true; // initially assume the game has been won
if (this.correctLetters.contains('_')) // if there are any '_'
won = false; // the game has not been won
return won;
/////////////////////////
// TODO FILL IN CODE HERE
/////////////////////////
// NOTE: THE LINE BELOW IS SO THE CODE WILL COMPILE
// Replace it with appropriate code for your implementation
//return false;
}
/**
* gameLost
*
* Return true if the user has lost the game;
* otherwise, return false.
*
* @return true if the user has lost, false otherwise
*/
private boolean gameLost() {
return this.incorrectLetters.size() >= 7;
/////////////////////////
// TODO FILL IN CODE HERE
/////////////////////////
// NOTE: THE LINE BELOW IS SO THE CODE WILL COMPILE
// Replace it with appropriate code for your implementation
//return false;
}
/**
* gameOver
*
* Return true if the user has either won or lost the game;
* otherwise, return false.
*
* @return true if the user has won or lost, false otherwise
*/
private boolean gameOver() {
return gameWon() || gameLost();
/////////////////////////
// TODO FILL IN CODE HERE
/////////////////////////
// NOTE: THE LINE BELOW IS SO THE CODE WILL COMPILE
// Replace it with appropriate code for your implementation
//return false;
}
/**
* printHangman
*
* Print the Hangman that corresponds to the given number of
* wrong guesses so far.
*
* @param numWrong number of wrong guesses so far
*/
private String printHangman() {
StringBuilder sb = new StringBuilder();
int poleLines = 6; // number of lines for hanging pole
System.out.println(" ____");
System.out.println(" | |");
sb.append(" ____\n");
sb.append(" | |\n");
int badGuesses = this.incorrectLetters.size();
if (badGuesses == 7) {
System.out.println(" | |");
System.out.println(" | |");
sb.append(" | |\n");
sb.append(" | |\n");
}
if (badGuesses > 0) {
System.out.println(" | O");
poleLines = 5;
sb.append(" | O\n");
}
if (badGuesses > 1) {
poleLines = 4;
if (badGuesses == 2) {
System.out.println(" | |");
sb.append(" | |\n");
} else if (badGuesses == 3) {
System.out.println(" | \\|");
sb.append(" | \\|\n");
} else if (badGuesses >= 4) {
System.out.println(" | \\|/");
sb.append(" | \\|/\n");
}
}
if (badGuesses > 4) {
poleLines = 3;
if (badGuesses == 5) {
System.out.println(" | /");
sb.append(" | /\n");
} else if (badGuesses >= 6) {
System.out.println(" | / \\");
sb.append(" | / \\\n");
}
}
if (badGuesses == 7) {
poleLines = 1;
}
for (int k = 0; k < poleLines; k++) {
System.out.println(" |");
sb.append(" |\n");
}
System.out.println("__|__");
System.out.println();
sb.append("__|__\n");
sb.append("\n");
return sb.toString();
}
//////////////////////////////////////////////////////////////////////
// 6. FOR TESTING PURPOSE ONLY
//////////////////////////////////////////////////////////////////////
/**
* toString
*
* Returns a string representation of the Hangman object.
* Note that this method is for testing purposes only!
* @return a string representation of the Hangman object
*/
public String toString() {
String s = "secret word: " + this.secretWord;
s += "\ncorrect letters: ";
for (int i = 0; i < this.correctLetters.size(); i++)
s += this.correctLetters.get(i) + " ";
s += "\nused letters: ";
for (int i = 0; i < this.incorrectLetters.size(); i++)
s += this.incorrectLetters.get(i) + " ";
s += "\n# bad letters: " + this.incorrectLetters.size();
return s;
}
private void setCurrentWord(String newWord) {
this.secretWord = newWord;
}
private void setCorrectLetters(ArrayList<Character> newGuesses) {
this.correctLetters = newGuesses;
}
private void setIncorrectLetters(ArrayList<Character> newUsedLetters) {
this.incorrectLetters = newUsedLetters;
}
private void setBadGuesses(int newBadGuesses) {
this.incorrectLetters.clear();
for (int i = 0; i < newBadGuesses; i++) {
this.incorrectLetters.add('x');
}
}
//////////////////////////////////////////////////////////////////////
// 7. PUBLIC CLASS METHOD - MAIN
//////////////////////////////////////////////////////////////////////
public static void main(String [] args) {
/* Note initially the constructor sets the secret word to:
* "miscellaneous". Be sure to update the constructor when
* you're ready to play the game.
*/
Hangman game = new Hangman();
/*
* A. Testing the constructor
*
* To test the constructor, we use the toString method
* to see if the data members are as expected.
*/
System.out.println("The CONSTRUCTED game is:\n" + game);
System.out.println("\n======== END CONSTRUCTOR TEST ========\n");
// */
/*
* B. Testing gameWon
*/
if (game.gameWon())
System.out.println("Game should not be won at beginning");
String str = "miscellaneous";
game.setCurrentWord(str);
ArrayList<Character> guesses = new ArrayList<Character>();
for (int i = 0; i < str.length(); i++)
guesses.add(str.charAt(i));
game.setCorrectLetters(guesses);
if (!game.gameWon()) {
System.out.println(game);
System.out.println("Game should be won");
}
for (int i = 0; i < str.length(); i += 3)
guesses.set(i, '_');
game.setCorrectLetters(guesses);
if (game.gameWon()) {
System.out.println(game);
System.out.println("Game should NOT be won");
}
System.out.println("\n======== END gameWon TEST ========\n");
// */
/*
* C. Testing gameLost
*/
game = new Hangman(); // start with a new game
if (game.gameLost())
System.out.println("Game should not be lost at beginning");
game.setBadGuesses(3);
if (game.gameLost()) {
System.out.println(game);
System.out.println("Game should not be lost");
}
game.setBadGuesses(7);
if (!game.gameLost()) {
System.out.println(game);
System.out.println("Game should be lost");
}
game.setBadGuesses(10);
if (!game.gameLost()) {
System.out.println(game);
System.out.println("Game should be lost");
}
System.out.println("\n======== END gameLost TEST ========\n");
// */
/*
* D. Testing gameOver
*
* Add your own similar tests as above.
*/
System.out.println("\n======== END gameOver TEST ========\n");
// */
/*
* E. Testing handleGuess
*/
game = new Hangman(); // start with a new game
System.out.println(game);
game.handleGuess('a'); // check a letter in the word
System.out.println(game);
game.handleGuess('q'); // check a letter not in the word
System.out.println(game);
game.handleGuess('m'); // check for first letter in word
System.out.println(game);
game.handleGuess('l'); // check a letter that appears more than once
System.out.println(game);
game.handleGuess('s'); // check last letter in word
System.out.println(game);
game.handleGuess('x'); // check another letter not in word
System.out.println(game);
System.out.println("\n======== END handleGuess TEST ========\n");
// */
/* F. Test the playGame method
* Do this after all the private methods have been tested.
*/
game = new Hangman(); // start with a new game
game.playGame();
// */
}
}
KnockKnockClient客户端:
import java.io.*;
import java.net.*;
public class KnockKnockClient {
public static void main(String[] args) throws IOException {
/*
needs two arguments, host name and port number.
*/
if (args.length != 2) {
System.err.println(
"Usage: java EchoClient <host name> <port number>");
System.exit(1);
}
/*
sets port number.
*/
String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);
/*
try resource creates new Socket object with hostname and portnumber to connect to.
*/
try (
Socket kkSocket = new Socket(hostName, portNumber);
PrintWriter out = new PrintWriter(kkSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(kkSocket.getInputStream()));
) {
BufferedReader stdIn =
new BufferedReader(new InputStreamReader(System.in));
String fromServer;
String fromUser;
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
} catch (UnknownHostException e) {
System.err.println("Don't know about host " + hostName);
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to " +
hostName);
System.exit(1);
}
}
}
答案 0 :(得分:1)
问题似乎在这里:
while ((fromServer = in.readLine()) != null) {
System.out.println("Server: " + fromServer);
if (fromServer.equals("Bye."))
break;
fromUser = stdIn.readLine();
if (fromUser != null) {
System.out.println("Client: " + fromUser);
out.println(fromUser);
}
}
简答:
服务器写入多行显示刽子手,只读一行,然后等待用户输入另一个字符。您应该阅读服务器输入的所有行。为此,请使用DataInputStream包装BufferedReader(dis = new DataInputStream(in)),然后使用dis.readUTF()获取整个服务器消息。
答案很长:
服务器会向您发送此字符串:____\n | |\n |\n |\n |\n.....
,您只能阅读第一个\n
。读取整个消息(直到in.readLine()为null或如上所述使用DataInputStream)然后询问客户端是否有新的猜测。
答案 1 :(得分:0)
由于您的客户端共享服务器控制台以进行输出,因此您应该同步每个客户端的输出
synchronized (this){
theOutput = hman.printCurrentState();
}
StringBuilder
没问题,因为创建了hman
的新实例。它不共享缓冲区。