import java.net.*; import java.io.*; import java.util.*; /** Kennzeichnet Fehler im Benutzerprotokoll */ class UserProtocolException extends Exception { /** Konstruiert einen Protokollfehler ohne nähere Angaben. */ public UserProtocolException() { super(); } /** Konstruiert einen Protokollfehler mit weiteren Angaben. @param s nähere Fehlerumstände */ public UserProtocolException(String s) { super(s); } } /** Erwartet eine TCP-Verbindungsanfrage (auf einem spezifizierbaren Port). Der Server bietet einen Zahlenratespieldienst an und obliegt folgendem Protokoll:
  1. Ausgabe der maximal zu erratenden Zahl (100).
  2. Warten auf Rateversuch (Timeout: 10s).
  3. Ausgabe, ob größer (">"), kleiner ("<") oder gleich ("=") der zu erratenden Zahl.
  4. Weiter mit 2., falls nicht gleich.
@see Client */ class Server { /** Defaultport für den angebotenen Dienst. */ public final static int DEFAULT_PORT = 6789; protected ServerSocket listen_socket; protected Socket client_socket; protected BufferedReader client_in; protected PrintWriter client_out; /** Wartet auf Kommunikationswünsche, akzeptiert einen Kommunikationswunsch und stößt den Dialog an. @param port Dienstportnummer */ public Server(int port) { try { listen_socket = new ServerSocket(port); while (true) { Socket client_socket = listen_socket.accept(); client_socket.setSoTimeout(10000); try { client_in = new BufferedReader( new InputStreamReader(client_socket.getInputStream())); client_out = new PrintWriter(client_socket.getOutputStream(), true); dialog(); } catch (IOException e) { System.err.println(e); } finally { try { if (client_in != null) client_in.close(); } catch (IOException e) { } if (client_out != null) client_out.close(); try { client_socket.close(); } catch (IOException e) { } } } } catch (IOException e) { System.err.println(e); } finally { try { if (listen_socket != null) listen_socket.close(); } catch (IOException e) { } } } /** Ausgabe der Zahlenobergrenze auf die TCP-Verbindung. */ void putmax() { client_out.println(100); } /** Warten auf einen Rateversuch. Der Timeout nach 10 Sekunden wird über den Sockettimeout realisiert. Alternativ wäre eine Schleife der Form denkbar. Ebenso ein Überwachungsthread für jede Verbindung, der die Verbindung nach 10s unterbricht. @return Rateversuch @exception UserProtocolException Formatfehler beim Lesen der geratenen Zahl @exception java.io.IOException Verbindungsfehler */ int gettrial() throws UserProtocolException, IOException { String line; int trial; line = client_in.readLine(); if (line == null) throw new UserProtocolException("Client closed connection."); try { trial = Integer.parseInt(line); } catch (NumberFormatException e) { throw new UserProtocolException("Protocol error."); } return trial; } /** Ausgabe des Antwortstrings auf die TCP-Verbindung. @param a Antwortstring */ void putanswer(String a) { client_out.println(a); } /** Protokollabwicklung @exception java.io.IOException Fehler in der Datenübertragung */ protected void dialog() throws IOException { int number; int trial; try { putmax(); // Bestimme eine Zufallszahl zwischen 1 und 100 // (0 <= Math.random() < 1, wie üblich) number = (int)(Math.random() * 100.0) + 1; do { trial = gettrial(); if (number == trial) putanswer("="); else if (number < trial) putanswer("<"); else putanswer(">"); } while (number != trial); } catch (UserProtocolException e) { System.err.println(e); } } public static void usage() { System.out.println("Usage: java Server [ ]"); System.exit(1); } /** Das Hauptprogramm ermittelt den gewünschten Dienstport aus der Kommandozeile (bei keiner Angabe wird DEFAULT_PORT verwendet) und stößt den Server an. @param args Kommandozeilenargumente @see DEFAULT_PORT @see Server */ public static void main(String[] args) { int port = DEFAULT_PORT; if (args.length > 1) usage(); if (args.length == 1) { try { port = Integer.parseInt(args[0]); } catch (NumberFormatException e) { usage(); } } new Server(port); } } /** Stellt eine Client-TCP-Verbindung zu einem Host-Rechner (auf einem spezifizierbaren Port) her. Der Client erwartet einen Zahlenratespielserver und gehorcht folgendem Protokoll:
  1. Erwarten einer Zahl auf der TCP-Verbindung.
  2. Schreiben einer Zahl auf die TCP-Verbindung.
  3. Erwarten eines ">", "<", "=" auf der TCP-Verbindung.
  4. Weiter mit 2., falls nicht "=" kam.
*/ class Client { /** Defaultport, auf dem ein passendes Serverprotokoll laufen sollte. */ public static final int DEFAULT_PORT = 6789; protected Socket socket; protected BufferedReader in; protected PrintWriter out; /** Kümmert sich um Verbindungsauf- und abbau. Stößt die Kommunikation und den Dialog an. @param host Hostname @param port Portnummer */ public Client(String host, int port) { try { socket = new Socket(host, port); try { in = new BufferedReader( new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); dialog(); } catch (IOException e) { System.err.println(e); } finally { try { if (in != null) in.close(); } catch (IOException e) { } if (out != null) out.close(); try { socket.close(); } catch (IOException e) { } } } catch (IOException e) { System.err.println(e); } finally { try { if (socket != null) socket.close(); } catch (IOException e) { } } } /** Holt die Zahlenobergrenze von der TCP-Verbindung. @return Zahlenobergrenze @exception UserProtocolException Formatfehler der Zahlenobergrenze @exception java.io.IOException Verbindungsfehler */ int getmax() throws UserProtocolException, IOException { String line; line = in.readLine(); if (line == null) throw new UserProtocolException("Server closed connection."); try { return (Integer.parseInt(line)); } catch (NumberFormatException e) { throw new UserProtocolException("Protocol error."); } } /** Holt einen Rateversuch von der Standardeingabe. @param trialno Nummer des Rateversuchs @return Rateversuch @exception java.io.IOException Fehler beim Lesen von der Standardeingabe */ int gettrial(int trialno) throws IOException { BufferedReader stdin = new BufferedReader( new InputStreamReader(System.in)); String line; int trial = 0; do { System.out.print("Dein " + trialno + ". Versuch? "); System.out.flush(); line = stdin.readLine(); try { trial = Integer.parseInt(line); } catch (NumberFormatException e) { trial = 0; } } while (trial == 0); return trial; } /** Ausgabe des Rateversuch auf die TCP-Verbindung. @param trial Rateversuch */ void puttrial(int trial) { out.println(trial); } /** Holt die Serverantwort von der TCP-Verbindung. Übersetzt die Serverantwort ("=", "<", ">") in Standard-strcmp-Format (0, -1, 1). @return 0: gleich, -1: kleiner, +1: größer @exception UserProtocolException andere Serverantwort als "=", "<", ">" @exception java.io.IOException Fehler beim Lesen von der TCP-Verbindung */ int getanswer() throws UserProtocolException, IOException { String line; line = in.readLine(); if (line == null) throw new UserProtocolException("Server closed connection."); if (line.startsWith("=")) return 0; else if (line.startsWith("<")) return -1; else if (line.startsWith(">")) return 1; else throw new UserProtocolException("Protocol error."); } /** Protokollabwicklung, sowohl auf der TCP-Verbindung als auch mit dem Benutzer.
  1. Informieren des Benutzers über die Zahlenobergrenze.
  2. Abfrage eines Rateversuch (versehen mit einer Nummer).
  3. Informieren des Benutzers über die Serverantwort.
  4. Weiter mit 2., falls Zahl nicht erraten.
@exception java.io.IOException TCP-Verbindungsstörung */ protected void dialog() throws IOException { String line; int max; int trial; try { max = getmax(); System.out.println( "Ich habe mir eine Zahl zwischen 1 und " + max + " ausgedacht!"); System.out.println("Kannst Du sie eraten?"); for (int trialno = 1; trialno != 0; ) { trial = gettrial(trialno); puttrial(trial); switch (getanswer()) { case 0: { int maxtrialno = (int)Math.ceil(Math.log(max)/Math.log(2.0)); System.out.println("Congratulations!"); System.out.println("Du hast " + trialno + " Versuche ben\"otigt!"); if (trialno > maxtrialno) System.out.println( "Es w\"are in " + maxtrialno + " zu schaffen gewesen"); trialno = 0; break; } case -1: { System.out.println("Meine Zahl ist kleiner!"); trialno++; break; } case 1: { System.out.println("Meine Zahl ist gr\"o\"ser!"); trialno++; break; } } } } catch (UserProtocolException e) { System.err.println(e); } } public static void usage() { System.out.println("Usage: java Client [ ]"); System.exit(1); } /** Das Hauptprogramm erschließt den Hostnamen und eventuell den Port aus den Kommandozeilenargumenten (wird kein Port angegeben, so wird DEFAULT_PORT verwendet). Stößt den Client an. @param args Kommandozeilenargumente @see DEFAULT_PORT @see Client */ public static void main(String[] args) { int port = DEFAULT_PORT; if ((args.length != 1) && (args.length != 2)) usage(); if (args.length != 1) { try { port = Integer.parseInt(args[1]); } catch (NumberFormatException e) { usage(); } } new Client(args[0], port); System.exit(0); } }