package EDU.gac.max.flipserver; import java.io.*; import java.net.*; import EDU.gac.max.io.*; /** This class is the main class for a server daemon program called FlipServer * that is used by the multi-user networked puzzle program NetFlip. * Each NetFlip communicates with the FlipServer through its NetInterface. * The communication protocol is very simple: the NetInterface sends the * number of a "key" (i.e., colored button) to be flipped to the FlipServer, * which turns around and sends it back to all the NetInterfaces (including * the originating one). The only exception from this "echoing" style of * operation is that the FlipServer keeps track of the board state (which * keys are flipped) and if a new client connects, immediately sends it the * key numbers to flip so that the new client's board looks like the others. * One ClientConnectionThread is used to listen on each client's connection. * *

Warning: this FlipServer class is missing some "synchronized" * keywords on some methods. This is intentional as part of a lab * assignment on race conditions and monitors for the MC78 course. * The students are supposed to find the race conditions and predict * the buggy behavior that can result, then verify by inserting * appropriate sleeps, then fix the problem by adding the needed * synchronized keywords. So, this class is known to be buggy. * *

Written 17 Aug 1996 by * Max Hailperin * <max@gac.edu> * * @see EDU.gac.max.flip.NetFlip * @see EDU.gac.max.flip.NetInterface * @see ClientConnectionThread */ public class FlipServer{ /** The addOut method is used to add another output stream that should * receive the flip notifications (key numbers); this is used when * a new client connects. Keys that were already flipped earlier are * also sent to the new stream. */ public void addOut(OutputStream newOut){ PrintStream ps = new PrintStream(newOut); for(int i = 0; i < size*size; i++){ if(state[i]){ ps.println(i); } } rawOut.add(newOut); } /** The removeOut method is used to remove one of the output streams * to which flip notifications (key numbers) are being sent; this is * used when the thread tending a particular client connection notices * that the client has closed the connection */ public void removeOut(OutputStream oldOut){ rawOut.remove(oldOut); } /** The flip method is used to record and transmit a flip; it is used * by a client connection thread that has received a flip number */ public void flip(int key){ state[key] = !state[key]; printOut.println(key); } public static void main(String[] args){ FlipServer server = new FlipServer(); try{ ServerSocket ss = new ServerSocket(port); while(true){ Socket s = ss.accept(); OutputStream newOut = s.getOutputStream(); server.addOut(newOut); (new ClientConnectionThread(server, s, newOut)).start(); } } catch(Exception e){ System.err.println(e); System.exit(1); } } private FlipServer(){ state = new boolean[size*size]; rawOut = new MulticastOutputStream(); printOut = new PrintStream(rawOut); } static final int size = 6; // how many keys wide and high static final int port = 8198; // agreed upon w/ NetInterface, unassigned private boolean[] state; private MulticastOutputStream rawOut; // stream leading to all the clients private PrintStream printOut; // also all the clients, but a PrintStream } /** This class is used by FlipServer to take care of listening for * flips comming in from a particular client (one thread per client, * namely one instance of this class per client). * *

Written 17 Aug 1996 by * Max Hailperin * <max@gac.edu> * * @see EDU.gac.max.flip.NetInterface * @see FlipServer */ class ClientConnectionThread extends Thread{ ClientConnectionThread(FlipServer srvr, Socket s, OutputStream o) throws IOException { server = srvr; sock = s; out = o; in = new DataInputStream(s.getInputStream()); } public void run(){ try{ while(true){ String line = in.readLine(); if(line == null) break; server.flip(Integer.parseInt(line)); } server.removeOut(out); } catch(Exception e){ System.err.println(e); } finally{ try{ sock.close(); } catch(Exception e){ System.err.println(e); } } } FlipServer server; Socket sock; OutputStream out; DataInputStream in; }