package altbit; import java.io.*; import java.net.*; import java.util.*; /** Sender implements the sending machine in the alternating bit protocol. * Instead of connecting directly to the receiving machine, a Sender * object first connects to a channel where activites such as losing * messages can be simulated. The channel from the receiving machine * must connect to the sender. */ public class Sender { public static final int SENDER_PORT = 5555; public static final int TIME_OUT = 2000; private int state = 1; private int sender_port; private String host = "localhost"; // Sockets to receive connection private ServerSocket ss; private Socket s_in; // Socket to make connection private Socket s_out; // Object InputStreams to read and write messages to Sender and Receiver private ObjectInputStream c2; private ObjectOutputStream c1; private volatile Thread t; private ProxyObjectReader pr; /** Creates a Sender object with the default port number. */ public Sender() { sender_port = SENDER_PORT; pr = new ProxyObjectReader(); t = new Thread(pr); } /** Creates a Sender object with the passed-in port number. */ public Sender(int port) { sender_port = port; pr = new ProxyObjectReader(); t = new Thread(pr); } /** Connects the Sender object to the channel listening at the * specified location. */ public boolean connect(String host, int port) { s_out = null; try { s_out = new Socket(host, port); c1 = new ObjectOutputStream(s_out.getOutputStream()); } catch (UnknownHostException e) { System.out.println("Host: " + host + " unknown or unavailable."); return false; } catch (IOException e) { System.out.println("Error occurred trying to connect to " + host + " at port " + port + "."); System.out.println(e); System.exit(0); } // If it works ... System.out.println("Connection to channel succeeded!"); return true; } /** Listens and connects the channel coming from the receiving * machine. */ public void accept() { try { ss = new ServerSocket(sender_port); s_in = null; s_in = ss.accept(); c2 = new ObjectInputStream(s_in.getInputStream()); } catch (IOException e) { System.out.println("Could not receive connection."); System.out.println(e); System.exit(0); } } /** Reads from the channel using a proxy reader. The java * input streams block indefinitely. Using a proxy reader * in a separate thread allows the read to be timed out. */ public Object readChannel() throws OptionalDataException, ClassNotFoundException, IOException{ Object o = pr.getObject(); if (o == null) throw new IOException(); else return o; } public static void main(String args[]) { Sender s = null; if (args.length == 0) { s = new Sender(); } else if (args.length == 1) { String str = args[0]; int s_port; try { s_port = Integer.parseInt(str); } catch (NumberFormatException e) { s_port = SENDER_PORT; } s = new Sender(s_port); } else { System.out.println("Usage: java Sender [port]"); System.exit(0); } // Make connection to channel boolean done = false; String hostname; int port; BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); while (!done) { System.out.println("Enter channel Hostname and port number:"); System.out.flush(); String str; try { str = input.readLine(); } catch (IOException e) { continue; } StringTokenizer st = new StringTokenizer(str); if (st.countTokens() != 2) { System.out.println("You must enter a hostname and port number.\n"); continue; } hostname = st.nextToken(); String p = st.nextToken(); try { port = Integer.parseInt(p); } catch (NumberFormatException e) { System.out.println("You must enter a valid port number.\n"); continue; } done = s.connect(hostname, port); } // Receive connection from sending channel System.out.println("Sender listening on port " + SENDER_PORT); System.out.flush(); s.accept(); System.out.println("Connection received."); boolean quitflag = false; Message m = null; Message ack = null; String msg = ""; s.t.start(); while (!quitflag) { switch (s.state) { case 1: System.out.println("SENDER IN STATE " + s.state); System.out.println("Enter message:"); try { msg = input.readLine(); } catch (IOException e) { ; } s.state = 2; break; case 2: System.out.println("SENDER IN STATE " + s.state); m = new Message(msg,0); System.out.println("Sending message with alt bit " + 0); try { s.c1.writeObject(m); } catch (IOException e) { ; } s.state = 3; break; case 3: System.out.println("SENDER IN STATE " + s.state); try { ack = (Message) s.readChannel(); } // Look for Errors in Stream catch (OptionalDataException e) { System.out.println("Error in reading from stream!"); s.state = 7; break; } catch (ClassNotFoundException e) { System.out.println("Error in reading from stream!"); s.state = 7; break; } // Look for Time Outs catch (IOException e) { System.out.println("Wait for Acknowledgement timed out."); s.state = 7; break; } // Look for wrong Alternating Bit if (ack.getAlternatingBit() != 0) { System.out.println("Wrong alternating bit in Acknowledgment."); s.state = 7; } else if (ack.getAlternatingBit() == 0) { System.out.println("Acknowledgement received : " + ack.getAlternatingBit()); s.state = 4; } break; case 4: System.out.println("SENDER IN STATE " + s.state); System.out.println("Enter message: 'q' to quit"); try { msg = input.readLine(); } catch (IOException e) { ; } if (msg.equals("q")) quitflag = true; s.state = 5; break; case 5: System.out.println("SENDER IN STATE " + s.state); m = new Message(msg,1); System.out.println("Sending message with alt bit " + 1); try { s.c1.writeObject(m); } catch (IOException e) { ; } s.state = 6; break; case 6: System.out.println("SENDER IN STATE " + s.state); try { ack = (Message) s.readChannel(); } // Look for Errors in Stream catch (OptionalDataException e) { System.out.println("Error in reading from stream!"); s.state = 8; break; } catch (ClassNotFoundException e) { System.out.println("Error in reading from stream!"); s.state = 8; break; } // Look for Time Outs catch (IOException e) { System.out.println("Wait for Acknowledgement timed out."); s.state = 8; break; } // Look for wrong Alternating Bit if (ack.getAlternatingBit() != 1) { System.out.println("Wrong alternating bit in Acknowledgment."); s.state = 8; } else if (ack.getAlternatingBit() == 1) { System.out.println("Acknowledgement received : " + ack.getAlternatingBit()); s.state = 1; } break; case 7: System.out.println("SENDER IN STATE " + s.state); System.out.println(" Will resend ..."); System.out.println("Sending message with alt bit 0"); try { s.c1.writeObject(m); } catch (IOException e) { ; } s.state = 3; break; case 8: System.out.println("SENDER IN STATE " + s.state); System.out.println(" Will resend ..."); System.out.println("Sending message with alt bit 1"); try { s.c1.writeObject(m); } catch (IOException e) { ; } s.state = 6; break; } } } /** Helper class to read from the channel. The java * input streams block indefinitely. Using this proxy reader * in a separate thread allows the read to be timed out. */ class ProxyObjectReader implements Runnable { private volatile Object obj = null; /** Return object read and reset for next read. */ public synchronized Object getObject() { if (obj == null) { try { wait(TIME_OUT); } catch (InterruptedException e) { System.out.println("Interrupted!!"); } } Object temp = obj; obj = null; return temp; } /** Method that is run when thread is started. */ public void run() { while(true) { try { obj = c2.readObject(); } catch (Exception e) { ; } synchronized(this) { notifyAll(); } } } } }