Реализация клиент-сервер, GUI StringBuilder проблема
Попытка сделать клиент-серверную программу, в частности игру палача.
У меня есть 4 файла: KnockKnockServer
, KnockKnockProtocol
,Hangman
а также KnockKnockClient.
KnockKnockServer
работает, KnockKnockClient
подключается к нему. KnockKnockProtocol
обрабатывает запрос от клиента. Клиент отправляет запрос от пользователя, если KnockKnockProtocol
распознать определенное слово или фразу, это даст ответ на конкретное слово. Файл Hangman является полным Hangman
игра (не клиент-сервер), и я хочу повторно использовать некоторые из ее методов, таких как печать чувак Hangman. Взять этого палача и показать на стороне клиента.
Поэтому я использую строитель строк, чтобы иметь возможность изменять строку. Но я получил странный результат. Должен ли я использовать строитель строк или что-то еще? Что еще мне нужно исправить, чтобы клиент мог получать ресурсы Hangman? Должен ли я изменить дизайн всего этого проекта?
Вот как распечатывает сервер:
run:
____
| |
|
|
|
|
|
|
__|__
_ _ _
Wrong letters: ____
| |
|
|
|
|
|
|
__|__
_ _ _
Wrong letters:
s
a
BUILD SUCCESSFUL (total time: 1 minute 31 seconds)
Просто странно, что он печатает палача два раза.
Вот что отображается на стороне клиента:
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);
}
}
}
2 ответа
Проблема, кажется, здесь:
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);
}
}
Короткий ответ:
Сервер пишет несколько строк для отображения палача, и вы читаете только одну строку, а затем вы ждете, пока пользователь введет другой символ. Вы должны прочитать все строки ввода сервера. Чтобы сделать это, оберните ваш BufferedReader DataInputStream ( dis = new DataInputStream(in)), затем используйте dis.readUTF(), чтобы получить сообщение всего сервера.
Длинный ответ:
Сервер отправляет вам эту строку: ____\n | |\n |\n |\n |\n.....
и вы читаете только до первого \n
, Прочитайте сообщение целиком (пока in.readLine() не имеет значения null или не используйте DataInputStream, как я уже говорил выше), а затем запросите у клиента новое предположение.
Поскольку ваши клиенты совместно используют консоль сервера для вывода, вы должны синхронизировать вывод для каждого клиента
synchronized (this){
theOutput = hman.printCurrentState();
}
StringBuilder
это нормально, потому что новый экземпляр hman
создано. Это не разделяет буфер.