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:
- Ausgabe der maximal zu erratenden Zahl (100).
- Warten auf Rateversuch (Timeout: 10s).
- Ausgabe, ob größer (">"), kleiner ("<") oder gleich ("=") der zu erratenden Zahl.
- 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
- bestimme aktuelle Uhrzeit at
- setze Timeoutzeit to auf at + 10s
- prüfe, ob der Eingabestrom bereit ist (ready()) oder der Timeout abgelaufen
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:
- Erwarten einer Zahl auf der TCP-Verbindung.
- Schreiben einer Zahl auf die TCP-Verbindung.
- Erwarten eines ">", "<", "=" auf der TCP-Verbindung.
- 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.
- Informieren des Benutzers über die Zahlenobergrenze.
- Abfrage eines Rateversuch (versehen mit einer Nummer).
- Informieren des Benutzers über die Serverantwort.
- 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); } }