From 47a7b1ec79bc9dd35b8bcbbb847c644be4c4e72a Mon Sep 17 00:00:00 2001 From: Max Goldman Date: Fri, 30 Oct 2015 10:18:34 -0400 Subject: [PATCH] Initial commit --- .classpath | 6 ++ .gitignore | 2 + .project | 17 +++++ src/square/SquareClient.java | 98 ++++++++++++++++++++++++ src/square/SquareQueue.java | 97 +++++++++++++++++++++++ src/square/SquareServer.java | 104 +++++++++++++++++++++++++ src/square/SquareServerMulti.java | 123 ++++++++++++++++++++++++++++++ 7 files changed, 447 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 src/square/SquareClient.java create mode 100644 src/square/SquareQueue.java create mode 100644 src/square/SquareServer.java create mode 100644 src/square/SquareServerMulti.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fb50116 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..739346c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +/bin diff --git a/.project b/.project new file mode 100644 index 0000000..8f0f214 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + ex22-square + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/src/square/SquareClient.java b/src/square/SquareClient.java new file mode 100644 index 0000000..9cb15a3 --- /dev/null +++ b/src/square/SquareClient.java @@ -0,0 +1,98 @@ +package square; + +import java.io.*; +import java.net.Socket; + +/** + * SquareClient is a client that sends requests to the SquareServer + * and interprets its replies. + * A new SquareClient is "open" until the close() method is called, + * at which point it is "closed" and may not be used further. + */ +public class SquareClient { + private Socket socket; + private BufferedReader in; + private PrintWriter out; + // Rep invariant: socket, in, out != null + + /** + * Make a SquareClient and connect it to a server running on + * hostname at the specified port. + * @throws IOException if can't connect + */ + public SquareClient(String hostname, int port) throws IOException { + socket = new Socket(hostname, port); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); + } + + /** + * Send a request to the server. Requires this is "open". + * @param x number to square + * @throws IOException if network or server failure + */ + public void sendRequest(int x) throws IOException { + out.print(x + "\n"); + out.flush(); // important! make sure x actually gets sent + } + + /** + * Get a reply from the next request that was submitted. + * Requires this is "open". + * @return square of requested number + * @throws IOException if network or server failure + */ + public int getReply() throws IOException { + String reply = in.readLine(); + if (reply == null) { + throw new IOException("connection terminated unexpectedly"); + } + + try { + return Integer.valueOf(reply); + } catch (NumberFormatException nfe) { + throw new IOException("misformatted reply: " + reply); + } + } + + /** + * Closes the client's connection to the server. + * This client is now "closed". Requires this is "open". + * @throws IOException if close fails + */ + public void close() throws IOException { + in.close(); + out.close(); + socket.close(); + } + + + + + private static final int N = 100; + + /** + * Use a SquareServer to square all the integers from 1 to N. + */ + public static void main(String[] args) { + try { + SquareClient client = new SquareClient("localhost", SquareServer.SQUARE_PORT); + + // send the requests to square 1...N + for (int x = 1; x <= N; ++x) { + client.sendRequest(x); + System.out.println(x + "^2 = ?"); + } + + // collect the replies + for (int x = 1; x <= N; ++x) { + int y = client.getReply(); + System.out.println(x + "^2 = " + y); + } + + client.close(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } +} diff --git a/src/square/SquareQueue.java b/src/square/SquareQueue.java new file mode 100644 index 0000000..e177289 --- /dev/null +++ b/src/square/SquareQueue.java @@ -0,0 +1,97 @@ +package square; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Squares integers. + */ +class Squarer { + + private final BlockingQueue in; + private final BlockingQueue out; + // Rep invariant: in, out != null + + /** + * Make a squarer that will listen for requests and generate replies. + * @param requests queue to receive requests from + * @param replies queue to send replies to + */ + public Squarer(BlockingQueue requests, BlockingQueue replies) { + this.in = requests; + this.out = replies; + } + + /** + * Start handling squaring requests. + */ + public void start() { + new Thread(new Runnable() { + public void run() { + while (true) { + // TODO: we may want a way to stop the thread + try { + // block until a request arrives + int x = in.take(); + // compute the answer and send it back + int y = x * x; + out.put(new SquareResult(x, y)); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + } + }).start(); + } +} + +/** + * An immutable squaring result message. + */ +class SquareResult { + private final int input; + private final int output; + + /** + * Make a new result message. + * @param input input number + * @param output square of input + */ + public SquareResult(int input, int output) { + this.input = input; + this.output = output; + } + + // TODO: we will want more observers, but for now... + + @Override public String toString() { + return input + "^2 = " + output; + } +} + +public class SquareQueue { + + /** + * Create and use a squarer. + */ + public static void main(String[] args) { + + BlockingQueue requests = new LinkedBlockingQueue<>(); + BlockingQueue replies = new LinkedBlockingQueue<>(); + + Squarer squarer = new Squarer(requests, replies); + squarer.start(); + + try { + // make a request + requests.put(42); + + // maybe do something concurrently... + + // read the reply + System.out.println(replies.take()); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } +} diff --git a/src/square/SquareServer.java b/src/square/SquareServer.java new file mode 100644 index 0000000..d627039 --- /dev/null +++ b/src/square/SquareServer.java @@ -0,0 +1,104 @@ +package square; +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * SquareServer is a server that squares integers passed to it. + * It accepts requests of the form: + * Request ::= Number "\n" + * Number ::= [0-9]+ + * and for each request, returns a reply of the form: + * Reply ::= (Number | "err") "\n" + * where a Number is the square of the request number, + * or "err" is used to indicate a misformatted request. + * SquareServer can handle only one client at a time. + */ +public class SquareServer { + /** Default port number where the server listens for connections. */ + public static final int SQUARE_PORT = 4949; + + private ServerSocket serverSocket; + // Rep invariant: serverSocket != null + + /** + * Make a SquareServer that listens for connections on port. + * @param port port number, requires 0 <= port <= 65535 + */ + public SquareServer(int port) throws IOException { + serverSocket = new ServerSocket(port); + } + + /** + * Run the server, listening for connections and handling them. + * @throws IOException if the main server socket is broken + */ + public void serve() throws IOException { + while (true) { + // block until a client connects + Socket socket = serverSocket.accept(); + try { + handle(socket); + } catch (IOException ioe) { + ioe.printStackTrace(); // but don't terminate serve() + } finally { + socket.close(); + } + } + } + + /** + * Handle one client connection. Returns when client disconnects. + * @param socket socket where client is connected + * @throws IOException if connection encounters an error + */ + private void handle(Socket socket) throws IOException { + System.err.println("client connected"); + + // get the socket's input stream, and wrap converters around it + // that convert it from a byte stream to a character stream, + // and that buffer it so that we can read a line at a time + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + // similarly, wrap character=>bytestream converter around the + // socket output stream, and wrap a PrintWriter around that so + // that we have more convenient ways to write Java primitive + // types to it. + PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); + + try { + // each request is a single line containing a number + for (String line = in.readLine(); line != null; line = in.readLine()) { + System.err.println("request: " + line); + try { + int x = Integer.valueOf(line); + // compute answer and send back to client + int y = x * x; + System.err.println("reply: " + y); + out.print(y + "\n"); + } catch (NumberFormatException e) { + // complain about ill-formatted request + System.err.println("reply: err"); + out.println("err"); + } + // important! flush our buffer so the reply is sent + out.flush(); + } + } finally { + out.close(); + in.close(); + } + } + + /** + * Start a SquareServer running on the default port. + */ + public static void main(String[] args) { + try { + SquareServer server = new SquareServer(SQUARE_PORT); + server.serve(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/square/SquareServerMulti.java b/src/square/SquareServerMulti.java new file mode 100644 index 0000000..119e6c3 --- /dev/null +++ b/src/square/SquareServerMulti.java @@ -0,0 +1,123 @@ +package square; +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * SquareServerMulti is a server that squares integers passed to it. + * It accepts requests of the form: + * Request ::= Number "\n" + * Number ::= [0-9]+ + * and for each request, returns a reply of the form: + * Reply ::= (Number | "err") "\n" + * where a Number is the square of the request number, + * or "err" is used to indicate a misformatted request. + * SquareServerMulti can handle multiple concurrent clients. + */ +public class SquareServerMulti { + /** Default port number where the server listens for connections. */ + public static final int SQUARE_PORT = 4949; + + private ServerSocket serverSocket; + // Rep invariant: serverSocket != null + // + // Thread safety argument: + // TODO SQUARE_PORT + // TODO serverSocket + // TODO socket objects + // TODO readers and writers in handle() + // TODO data in handle() + + /** + * Make a SquareServerMulti that listens for connections on port. + * @param port port number, requires 0 <= port <= 65535 + */ + public SquareServerMulti(int port) throws IOException { + serverSocket = new ServerSocket(port); + } + + /** + * Run the server, listening for connections and handling them. + * @throws IOException if the main server socket is broken + */ + public void serve() throws IOException { + while (true) { + // block until a client connects + final Socket socket = serverSocket.accept(); + // create a new thread to handle that client + Thread handler = new Thread(new Runnable() { + public void run() { + try { + try { + handle(socket); + } finally { + socket.close(); + } + } catch (IOException ioe) { + // this exception wouldn't terminate serve(), + // since we're now on a different thread, but + // we still need to handle it + ioe.printStackTrace(); + } + } + }); + // start the thread + handler.start(); + } + } + + /** + * Handle one client connection. Returns when client disconnects. + * @param socket socket where client is connected + * @throws IOException if connection encounters an error + */ + private void handle(Socket socket) throws IOException { + System.err.println("client connected"); + + // get the socket's input stream, and wrap converters around it + // that convert it from a byte stream to a character stream, + // and that buffer it so that we can read a line at a time + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + // similarly, wrap character=>bytestream converter around the + // socket output stream, and wrap a PrintWriter around that so + // that we have more convenient ways to write Java primitive + // types to it. + PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true); + + try { + // each request is a single line containing a number + for (String line = in.readLine(); line != null; line = in.readLine()) { + System.err.println("request: " + line); + try { + int x = Integer.valueOf(line); + // compute answer and send back to client + int y = x * x; + System.err.println("reply: " + y); + out.println(y); + } catch (NumberFormatException e) { + // complain about ill-formatted request + System.err.println("reply: err"); + out.print("err\n"); + } + // important! our PrintWriter is auto-flushing, but if it were not: + // out.flush(); + } + } finally { + out.close(); + in.close(); + } + } + + /** + * Start a SquareServerMulti running on the default port. + */ + public static void main(String[] args) { + try { + SquareServerMulti server = new SquareServerMulti(SQUARE_PORT); + server.serve(); + } catch (IOException e) { + e.printStackTrace(); + } + } +}