实现客户端 - 服务器,GUI StringBuilder问题

时间:2013-11-17 11:08:07

标签: java sockets java-ee client-server stringbuilder

尝试制作客户端服务器程序,特别是一个刽子手游戏。

我有4个文件:KnockKnockServerKnockKnockProtocolHangmanKnockKnockClient.

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);
        }
    }
}

2 个答案:

答案 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的新实例。它不共享缓冲区。