IM269 Coursework 2: EchoClient.java


// IM269 - Coursework 2: Echo Client/Server
// Semester A, 2nd November 1998
// Eamonn Martin (BSc Computing)
// Student ID: 96/D59682
// efm001@unl.ac.uk

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;

// Binary dialog window (yes/no, true/false, retry/cancel, etc)
class BinaryDialog extends Dialog {
	private boolean response;			// Dialog response flag

	// Constructor - build the dialog window
	public BinaryDialog(Frame parent, String title, String msg, String y, String n) {
		super(parent, title, true);
		TextArea m;
		Button b;
		Panel p;
		p = new Panel(new BorderLayout());	// Message panel
		p.add(m = new MessageArea(msg, TextArea.SCROLLBARS_NONE));
		m.setBackground(Color.yellow);		// Set message background
		add(p);					// Add message panel
		p = new Panel();			// Button panel
		ActionListener a = new ActionListener() { // Button handler
			public void actionPerformed(ActionEvent e) {
				response = (e.getActionCommand()=="1");
				dispose();		// Close the dialog
			}
		};
		p.add(b = new Button(y) {		// Positive-response button
			public String getActionCommand() { return "1"; }
		});
		b.addActionListener(a);			// Set button handler
		p.add(b = new Button(n) {		// Negative-response button
			public String getActionCommand() { return "0"; }
		});
		b.addActionListener(a);			// Set button handler
		add(p, "South");			// Add button panel
		pack();					// Initialize layout
		addWindowListener(new WindowAdapter() {	// Enable window-closing
			public void windowClosing(WindowEvent e) {
				response = false;	// Negative response
				dispose();		// Close the dialog
			}
		});
	}

	// Prevent dialog resizing
	public boolean isResizable() { return false; }

	// Return the value of the response flag set by the dialog
	public boolean getResponse() { return response; }
}


// Single-shot timed-delay object
class TimeOut extends Thread {
	private boolean asleep;				// Sleeping status flag
	private long millis;				// Sleep time
	public TimeOut(long millis) {			// Constructor
		this.millis = millis;			// Set sleep-time
	}
	public void start() {				// Start the countdown
		asleep = true;				// Set asleep-flag
		super.start();				// Start this thread
	}
	public void run() {				// Start sleeping
		try { sleep(millis); }			// Suspend this thread
		catch (InterruptedException e) {}	// Ouch!
		asleep = false;				// Reset asleep-flag
	}
	public boolean isAsleep() { return asleep; }	// Return asleep status
}

// EchoClient window
class ClientWindow extends AppWindow {
	private TextArea text, echo;			// Text input and echo
	private TextField server, status;		// Server and status message
	private String host = "127.0.0.1";		// Default server host
	private int port = 9999;			// Default server port

	// Constructor - build window with title and initial server
	ClientWindow(String title, String srv) {
		super(title);
		Panel p, q;
		Button b;

		q = new Panel(new GridLayout(2, 1));	// Text I/O panel
		p = new Panel(new BorderLayout());	// Input panel
		p.add(new Label(" Input:"), "North");	// Input label
		p.add(text = new TextArea());		// Input area
		q.add(p);				// Add to text panel
		p = new Panel(new BorderLayout());	// Echo panel
		p.add(new Label(" Response:"), "North");// Echo label
		p.add(echo = new MessageArea());	// Echo area
		q.add(p);				// Add to text panel
		add(q);					// Add to window

		q = new Panel(new GridLayout(2, 1));	// Top panel (server/buttons)
		p = new Panel(new BorderLayout());	// Server panel
		p.add(new Label(" Echo Server:"), "West");
		p.add(server = new TextField(srv));	// Server field
		server.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) { server.selectAll(); }
			public void focusLost(FocusEvent e) { server.select(0, 0); }
		});
		server.setText(getServer());		// Set initial/default server
		q.add(p);				// Add to top panel

		p = new Panel(new GridLayout());	// Button panel
		b = new Button("Transmit");		// Transmit button
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { transmit(); }
		});
		p.add(b);				// Add to button panel
		b = new Button("Quit");			// Quit button
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { dispose(); }
		});
		p.add(b);				// Add to button panel
		q.add(p);				// Add to top panel
		add(q, "North");			// Add to window

		p = new Panel(new BorderLayout());	// Status panel
		p.add(status = new MessageField());	// Status message
		add(p, "South");			// Add to window

		host = getHost();			// Update default host
		port = getPort();			// Update default port
		text.setText("Send me!");		// Default text for convenience
		setBackground(SystemColor.control);	// Set background colour
		setSize(350, 250);			// Set window size
		setVisible(true);			// Display the window
	}

	// Wait a specified time for a character from an input stream
	private int readWait(long millis, InputStream i) {
		TimeOut t = new TimeOut(millis);	// Timed-delay object
		t.start();				// Start the timer
		try { while (i.available() < 1)		// Wait for a character
			if (!t.isAsleep()) return -1;	// Timout occurred?
			t.stop();			// Stop the timer
			return i.read();		// Return the character
		} catch (IOException e) {}
		return -1;				// I/O error - fail
	}

	// Display a retry/cancel dialog and return the response
	private boolean retryDialog() {
		int w = 250, h = 200;			// Dialog dimensions
		BinaryDialog d;				// Binary dialog window
		d = new BinaryDialog(this, "EchoClient: " + getServer(),
			"\nThis server is not responding!\n\nClick 'Retry' to " +
			"continue waiting for a response from the server.\n\n" +
			"Click 'Cancel' to abort this transaction.", " Retry ", "Cancel");
		Dimension s = getToolkit().getScreenSize();
		d.setBounds(s.width/2 - w/2, s.height/2 - h/2, w, h);
		d.setVisible(true);			// Display the dialog
		return d.getResponse();			// User's response
	}

	// Write string to output stream and read back from input stream
	private String echoString(String s, Socket socket) {
		DataInputStream in = null;
		DataOutputStream out = null;
		try {					// Write/read string
			out = new DataOutputStream(socket.getOutputStream());
			in = new DataInputStream(socket.getInputStream());
			String buf = "";		// String buffer
			out.writeBytes(s);		// Write string
			for (int c, i=s.length(); i > 0; --i) {
				do {			// Loop until all echoed
					c = readWait(10000, in);	// Wait 10-secs
					if (c < 0) if (!retryDialog())	// No echo?
						throw new IOException();// Give up
				} while (c < 0);	// Continue waiting?
				buf += (char)c;		// Append character to buffer
			}
			echo.setText(s = buf);		// Set echo text
		} catch (IOException e) {		// Operation failed
			s = null;
		} finally {				// Close i/o streams
			try { if (out!=null) out.close(); }
			catch (IOException e) { s = null; }
			try { if (in!=null) in.close(); }
			catch (IOException e) { s = null; }
		}
		return s;
	}

	// Transmit text to echo-server
	private void transmit() {
		String s;
		Socket socket;
		echo.setText("");			// Clear current echo text
		server.setText(getServer());		// Set current host:port
		report("Contacting server...");		// Begin echo transaction
		try {					// Create the client socket
			socket = new Socket(getHost(), getPort());
		} catch (IOException e) {		// No socket
			report("Unable to contact server!");
			return;				// Abort
		}
		report("Transmitting...");		// Transmit/receive text
		if ((s = echoString(text.getText(), socket))!=null) echo.setText(s);
		report((s!=null) ? "Transaction complete." : "EchoClient I/O error!");
		try { socket.close(); } catch (IOException e) {}
	}

	// Return current host (specified or default)
	private String getHost() {
		String s = server.getText();		// Get text in server field
		int i = s.indexOf(':');			// Locate port specifier
		if (i>=0) s = s.substring(0, i);	// Remove port specification
		return (s.length() > 0) ? s : host;	// Specified or default host
	}

	// Return current port (specified or default)
	private int getPort() {
		String s = server.getText();		// Get text in server field
		int i = s.indexOf(':');			// Locate port specifier
		if ((i>=0) && (s.length() > i+1)) try {	// Read port number?
			return (Integer.decode(s.substring(i+1))).intValue();
		} catch (NumberFormatException e) {}	// Invalid port number
		return port;				// Default port
	}

	// Return the complete host:port string for the server
	private String getServer() { return getHost() + ":" + getPort(); }

	// Write a string to the status message area
	void report(String s) { status.setText(s); }
}

// Create the client window
public class EchoClient {
	private static String info =
	    "\nIM269 - Coursework 2: EchoClient\nSemester A, 2nd November 1998\n" +
	    "Eamonn Martin (BSc Computing)\nStudent ID: 96/D59682\nefm001@unl.ac.uk\n\n" +
	    "EchoClient [host][:port]\n\n" +
	    "The default address used for the echo-server is 127.0.0.1:9999 (loopback).\n" +
	    "A different default host and/or port may be specified on the command-line.\n" +
	    "The current server may be changed by modifying the 'Echo Server' field.\n\n" +
	    "A full listing of this program can be found on the World Wide Web at:\n\n" +
	    "\t\t    http://www2.unl.ac.uk/~efm001/im269/\n\n" +
	    "Starting EchoClient...";

	// Print info and create client window
	public static void main(String[] args) {
		System.out.println(info);
		String srv = (args.length > 0) ? args[0] : "";
		new ClientWindow("EchoClient", srv).report("EchoClient Ready");
	}
}

Go To: IM269: Programming The Internet