Stock Market Quotes – Socket Version The screen will display a stock market ticker We will reuse our scrolling banner ! We will develop two different approaches Client Pull – the client periodically contacts the server and requests updates Server Push – the client supplies a list of stocks it is watching and the server notifies the client whenever one of these stock changes We develop the same two approaches use RMI (Remote Method Invocation) in the next section
The Overall Design Here is the class diagram showing the new classes Here is how the program operates The client contacts the server for an update The server sends back a complete list of (name quote) pairs for all stocks The client picks out those it wants to display
Fields and Methods in the Ticker Class
A Generic Ticker Banner Class - 1 public class Ticker extends DBAnimationApplet { public void initQuotes() {} public void initAnimator() { String att = getParameter("delay"); if (att != null) { setDelay(Integer.parseInt(att)); } watch = getParameter("watch"); if (att != null) { StringTokenizer tk = new StringTokenizer(watch); Vector v = new Vector(); while (tk.hasMoreTokens()) { v.addElement(tk.nextToken()); } n = v.size(); symbol = new String[n]; quote = new String[n]; for (int i = 0; i < n; i++) { symbol[i] = (String) v.elementAt(i); quote[i] = "0"; } url = getDocumentBase(); initQuotes(); updateQuotes(); text1 = text2; x = d.width; y = font.getSize();
A Generic Ticker Banner Class - 2 public void paintFrame(Graphics g) { g.setColor(Color.black); g.fillRect(0,0,d.width,d.height); // set the font and color, and draw the text g.setFont(font); g.setColor(Color.green); g.drawString(text1 + text2, x, y); // get the font metrics to determine the length of the text FontMetrics fm = g.getFontMetrics(); int length = fm.stringWidth(text1); // adjust the position of text for the next frame x -= offset; // if the text is completely off to the left end // move the position back to the right end if (x < -length) { x = 0; text1 = text2; updateQuotes(); }
A Generic Ticker Banner Class - 3 protected synchronized void updateQuotes() { StringBuffer sb = new StringBuffer(); for (int i = 0; i < n; i++) { sb.append(symbol[i] + " " + quote[i] + " "); } text2 = sb.toString(); protected String text1, text2; protected Font font = new java.awt.Font("Helvetica", Font.BOLD, 24); protected int speed = 100; protected int offset = 1; protected int x, y; protected int n; protected String symbol[], quote[]; protected URL url; protected String watch;
TickerPullClient public class TickerPullClient extends Ticker { protected void updateQuotes() { int i; try { Socket t = new Socket(url.getHost(), 8001); BufferedReader in = new BufferedReader(new InputStreamReader(t.getInputStream())); String line; while ((line = in.readLine()) != null) { StringTokenizer tk = new StringTokenizer(line); String name = tk.nextToken(); for (i = 0; i < n; i++) { if (symbol[i].equals(name)) { String newquote = tk.nextToken(); quote[i] = newquote; } t.close(); } catch (IOException e) {} super.updateQuotes();
The Server Side - 1 /* Protocol --- Ticker client pull Port: 8001 Client: connect Server: name1 quote1 name2 quote2 ... Server: disconnect Server design: two threads: client handler, quote generator no synchronization necessary for quote[] */ public class QuotePullServer extends Thread { static protected String symbol[] = { "IBM", "Sun", "Intel", "Apple", "Compaq" }; static protected int quote[] = { 100, 100, 100, 100, 100 }; static protected int n = 5; The Server Side - 1
The Server Side - 2 public static void main(String[] args) { System.out.println("QuotePullServer started."); new QuotePullServer().start(); try { ServerSocket s = new ServerSocket(8001); for (;;) { Socket incoming = s.accept(); PrintWriter out = new PrintWriter( new OutputStreamWriter(incoming.getOutputStream())); for (int i = 0; i < n; i++) { out.println(symbol[i] + " " + quote[i]); } out.flush(); out.close(); incoming.close(); } catch (Exception e) { System.err.println(e); System.out.println("QuotePullServer stopped."); The Server Side - 2
The Server Side - 3 public void run() { while (true) { try { Thread.currentThread().sleep(10000); System.out.println("New quote:"); for (int i = 0; i < n; i++) { quote[i] += (Math.random()-0.4)*( 10.0*Math.random()); System.out.println(symbol[i] + " " + quote[i]); } System.out.println("End quote."); } catch (Exception e) { System.err.println(e);
The html File <title>Java Applet Demo: Ticker</title> <h1>Ticker</h1> <hr> <applet code="TickerPullClient.class" width=200 height=30> <param name=delay value="100"> <param name=watch value="Sun Intel IBM"> </applet> <p> <param name=speed value="100"> <param name=watch value="Sun Apple Compaq"> <a href="TickerPullClient.java">The pull client.</a> <br> <a href="QuotePullServer.java">The pull server.</a> <br>
Server Push Implementation The sequence of operations
Server Push Design Notice that we reuse our generic Ticker class
The Client Side /* Protocol --- Ticker client pull Port: 8002 Client: connect Client: WATCH name1 name2 ... Server: name1 quote1 // send all quotes the first time name2 quote2 ... Server: DONE Server: name quote // resend quote when changed Server: name quote Client: CLOSE Client design: two threads: animator, quote listener animator waits until all quotes are in no synchronization necessary for quote[] */ The Client Side
TickerPushClient - 1 public class TickerPushClient extends Ticker { protected Socket socket; protected PrintWriter out; protected QuoteListener quotelistener; public void initQuotes() { try { socket = new Socket(url.getHost(), 8002); out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); out.println("WATCH " + watch); out.flush(); System.out.println("Send: " + "WATCH " + watch); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while ((line = in.readLine()) != null) { System.out.println("Receive: " + line); if (line.trim().equals("DONE")) { break; }
TickerPushClient - 2 StringTokenizer tk = new StringTokenizer(line); String name = tk.nextToken(); for (int i = 0; i < n; i++) { if (symbol[i].equals(name)) { String newquote = tk.nextToken(); quote[i] = newquote; } out.flush(); quotelistener = new QuoteListener(this, in); quotelistener.start(); } catch (IOException e) { System.err.println(e); public void destroy() { out.println("CLOSE"); quotelistener.interrupt();
QuoteListener class QuoteListener extends Thread { public QuoteListener(TickerPushClient ticker, BufferedReader in) { this.ticker = ticker; this.in = in; } public void run() { String line; try { while ((line = in.readLine()) != null) { System.out.println("Receive: " + line); StringTokenizer tk = new StringTokenizer(line); String name = tk.nextToken(); for (int i = 0; i < ticker.n; i++) { if (ticker.symbol[i].equals(name)) { String newquote = tk.nextToken(); ticker.quote[i] = newquote; if (isInterrupted()) { break; } in.close(); } catch (IOException e) { System.out.println(e); QuoteListener
QuotePushServer - 1 public class QuotePushServer extends Thread { static protected String symbol[] = { "IBM", "Sun", "Intel", "Apple", "Compaq" }; static protected int quote[] = { 100, 100, 100, 100, 100 }; static protected int n = 5; static protected Vector clients = new Vector(); public static void main(String[] args) { System.out.println("QuotePullServer started."); new QuotePushServer().start(); try { ServerSocket s = new ServerSocket(8002); // Waiting for new clients for (;;) { Socket incoming = s.accept(); ClientHandler newClient = new ClientHandler(incoming); clients.addElement(newClient); newClient.start(); } } catch (Exception e) { System.err.println(e); System.out.println("QuotePullServer stopped."); QuotePushServer - 1
QuotePushServer - 2 static public int getQuote(String name) { for (int i = 0; i < n; i++) { if (symbol[i].equals(name)) ( return quote[i]; } } return 0; // update quote public void run() { while (true) { try { Thread.currentThread().sleep(10000); System.out.println("New quote:"); int dq = 0; if (Math.random() < 0.5) { dq = (int) ((Math.random() - 0.4) * ( 10.0 * Math.random())); if (dq > 0) { quote[i] += dq; System.out.println("Changed: " + symbol[i] + " " + quote[i]); Enumeration enum = clients.elements(); while (enum.hasMoreElements()) { ClientHandler t = (ClientHandler) enum.nextElement(); t.updateQuote(symbol[i], quote[i]); System.out.println("End quote."); } catch (Exception e) { System.err.println(e); } QuotePushServer - 2
ClientHandler - 1 class ClientHandler extends Thread { protected PrintWriter out; protected BufferedReader in; protected Socket socket; protected String symbol[]; protected int n; public ClientHandler (Socket socket) { this.socket = socket; try { out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { System.err.println(e); } public void updateQuote(String name, int quote) // see second slide public void run() // see third slide ClientHandler - 1
ClientHandler - 2 public void updateQuote(String name, int quote) { boolean needToSend = false; int i; if (symbol != null) { for (i = 0; i < n; i++) { if (symbol[i].equals(name)) { needToSend = true; break; } } else { if (needToSend) { out.println(name + " " + quote); out.flush();
ClientHandler - 3 Run responds to two messages: WATCH and CLOSE public void run() { try { String line; while ((line = in.readLine()) != null) { System.out.println("Receive: " + line); if (line.startsWith("WATCH")) { StringTokenizer tk = new StringTokenizer(line); tk.nextToken(); Vector v = new Vector(); while (tk.hasMoreTokens()) { v.addElement(tk.nextToken()); } n = v.size(); symbol = new String[n]; int i; for (i = 0; i < n; i++) { symbol[i] = (String) v.elementAt(i); } for (i = 0; i < n; i++) { out.println(symbol[i] + " " QuotePushServer.getQuote(symbol[i])) } out.println("DONE"); out.flush(); } else if (line.trim().equals("CLOSE")) { break; } } socket.close(); in.close(); out.close(); } catch (IOException e) { System.err.println(e); QuotePushServer.clients.removeElement(this); Run responds to two messages: WATCH and CLOSE ClientHandler - 3
Using Sockets for a Chat Room Briefly describe your approach to solve the following problem
Use Sockets to Play Tic-Tac-Toe Describe how sockets could be used to adapt the tic-tac-toe game so it can be played from two different clients remotely