Home : Course Map : Chapter 15 :
Run the DataClient Program
JavaTech
Course Map
Chapter 15

Client/ServerDesign
Processing Steps
Data Server
Run Data Server
    Demo 1
Data Client
DataClient Applet
    Demo 2
Client/Server Sim
SimServer
    Demo 3
SimClient
Run SimClient
    Demo 4
Exercises

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

The DataClient program, discussed in detail on the previous page, can run either as an applet or a application. Below it is shown as an applet. The complete code listings of DataClient and DataClientWorker classes are also provided.

Note: This applet generates a security exception when it attempts to open a socket to the DataServer unless the server is running on the same machine as the applet. (Also, the host address would need to be changed to the address of the host from which the applet was downloaded.)

For your own experimentation, you should download the files for both the client and server to your machine. Then compile and run them there. (You can use the host address 127.0.0.1 that points to the local machine.)

DataClient Applet


Image of
DataClient in action


DataClient.java - Create an interface for the user to initiate a connection with the server at the given host address. It communicates via a socket at port 2222. The user name is required for the login. A text area displays messages generated for different steps in the setup and communications with the server.

The server sends a set of data for each request. The top histogram shows the values in each data channel in each set of data. The lower histogram displays a distribution of values for one channel over many sets of data.

+ Helper class:
DataClientWorker.java
- this Thread subclass communicates with the server and periodically requests a set of data. When it receives the data it passes it to the DataClient_JApplet11 parent, which then displays the data in the histograms.

+ Previous classes:
Ch.8:Tech:
HistogramStatR1.java, HistogramAdaptR1.java
Ch.6:Tech: Histogram.java, HistPanel.java
Ch.6:Tech: PlotPanel.java, PlotFormat.java

 

package DataMonitor;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;

/**
  *
  *  A Prototype Remote Data Monitoring System
  *
  *  DataClientdisplays data sent by DataServer. At a given time
  *  interval, DataClient requests a set of data from the server.
  *  The server sends first an integer for the number of data points
  *  to be sent and then sends that many integer values.
  *
  *  DataClient runs both as a standalone app or in a browser web page.
  *
  *  The User Interface displays several items of interest:
  *   - The host IP and username are displayed in TextFields
  *   - Stop/Stop button can interrupt and restart the connection.
  *   - A label displays the status of the connection
  *   - a TextArea displays the current data.
  *   - One histogram displays the values in each channel of a data set.
  *   - The second histogram displays the distribution of values for one
  *     channel of data. The number for the desired channel is entered
  *     into a text field.
  *
  *  The thread class fDataClientWorker is used to send  requests for
  *  data and to read the data. The fDataClientWorker consists primarily
  *  of a run () method. It calls back to the setData (int []) method
  *  to send the data set obtained fromthe server.
 **/
public class DataClient extends JApplet
             implements ActionListener
{
  // GUI setup

  // First histogram will show the data values in each set
  // obtained from the server.
  HistPanel fHistDataPanel = null;
  Histogram fHistData = null;

  // Second histogram plots the values of one channel in the
  // data set.
  HistPanel fHistChanPanel   = null; // Histogram of a data channel
  HistogramAdaptR1 fHistChan = null;

  // Initial array size in HistogramAdaptR1 to hold values.
  int fHistArraySize = 1000;

  // UI components
  JLabel     fStatusLabel      = null;
  JTextField fHostField        = null;
  JTextField fUserNameField    = null;
  JTextField fChanField        = null;
  JTextArea  fMessageArea      = null;

  // Flag for whether the applet is in a browser
  // or running via the main () below.
  boolean fInBrowser=true;

  //Buttons
  JButton fStartButton = null;
  JButton fClearButton = null;
  JButton fExitButton  = null;

  // Networking setup

  // Properties needed for the connection setup
  Socket fServer = null;
  DataClientWorker fDataClientWorker = null;

  int fDataServerPort     = 2222; // use 2222 as default
  String fClientPorfHostt = "";
  String fHost            = "127.0.0.1";
  String fUserName        = "Smith";
  int fChannelToMonitor   = 0;

  // Amount of data in each set read from the server
  int fNumDataChannels = 10;

  // flag for connection status
  boolean fConnected = false;

  // read data every 500 msecs
  int fTimeUpdate = 500;

  /**
    * Create a User Interface with a textarea with sroll bars
    *  and a Go button to initiate processing and a Clear button
    * to clear the textarea.
   **/
  public void init () {

    Container content_pane = getContentPane ();

    // Histograms:

    // First hist used to show the values in the data set
    // that is obtained in each transfer from the server.
    fHistData = new Histogram ("Data set values",
                             "Channel Number",
                              10, 0.0, 10.0);
    fHistDataPanel = new HistPanel (fHistData);


    // Second histogram displays the distribution of values
    // for one of the data channels. Use an adaptable histogram
    // since different channels could have different data ranges.
    fHistChan = new HistogramAdaptR1 ("Channel",
                           "Data Units",
                           50, 0.0, 50.0,
                           fHistArraySize);
    fHistChanPanel = new HistPanel (fHistChan);

    // The two hists go onto a sub-panel.
    JPanel hists_panel = new JPanel (new GridLayout (2,1));
    hists_panel.add (fHistDataPanel);
    hists_panel.add (fHistChanPanel);

    // Control fields:

    // First provide the inputs field for the host IP
    // and for the user name.
    fHostField = new JTextField (fHost,16);
    JLabel host_label = new JLabel ("Host: ");
    host_label.setHorizontalAlignment (SwingConstants.RIGHT);
    fUserNameField    = new JTextField (fUserName,16);
    JLabel name_label = new JLabel ("User Name: ");
    name_label.setHorizontalAlignment (SwingConstants.RIGHT);

    // Top line of controls = host and name inputs
    JPanel ctrls_panel1 = new JPanel ();
    ctrls_panel1.add (host_label);
    ctrls_panel1.add (fHostField);
    ctrls_panel1.add (name_label);
    ctrls_panel1.add (fUserNameField);

    // Next line holds the buttons, and the data
    // channel number to monitor.
    fStartButton = new JButton ("Start");
    fStartButton.addActionListener (this);
    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);
    fExitButton = new JButton ("Exit");
    if (fInBrowser)
        fExitButton.setEnabled (false);
    else
        fExitButton.addActionListener (this);

    JPanel buttons_panel = new JPanel ();
    buttons_panel.add (fStartButton);
    buttons_panel.add (fClearButton);
    buttons_panel.add (fExitButton);

    JLabel chan_label = new JLabel ("Channel: ");
    chan_label.setHorizontalAlignment (SwingConstants.RIGHT);
    fChanField = new JTextField ("0",5);

    JPanel chan_panel = new JPanel ();
    chan_panel.add (chan_label);
    chan_panel.add (fChanField);

    fStatusLabel = new JLabel ("Disconnected");
    fStatusLabel.setForeground (Color.RED);
    fStatusLabel.setHorizontalAlignment (SwingConstants.CENTER);

    // Now pack the components of the second ctrls
    // line of components
    JPanel ctrls_panel2 = new JPanel ();
    ctrls_panel2.add (buttons_panel);
    ctrls_panel2.add (chan_panel);
    ctrls_panel2.add (fStatusLabel);

    // Put the 2 lines of controls into a sub-panel
    JPanel ctrls_panel12 = new JPanel ();
    ctrls_panel12.add (ctrls_panel1);
    ctrls_panel12.add (ctrls_panel2);

    fMessageArea = new JTextArea ();
    fMessageArea.setEditable (false);
    // Add to a scroll pane so that a long list of
    // computations can be seen.
    JScrollPane area_scroll_pane = new JScrollPane (fMessageArea);

    // Use a GridBagLayout to apportion space for the
    // controls, text area and histograms.
    JPanel main_panel = new JPanel (new GridBagLayout ());

    GridBagConstraints c = new GridBagConstraints ();
    c.fill = GridBagConstraints.BOTH;


    // Put ctrls at top
    c.gridx = 0;
    c.gridy = 0;
    c. weightx = 1.0;
    c. weighty = 0.05;
    main_panel.add (ctrls_panel12,c);

    // Put text area below the controls
    c.gridx = 0;
    c.gridy = 1;
    c. weightx = 1.0;
    c. weighty = 0.25;
    main_panel.add (area_scroll_pane, c);

    // Put histograms in rest of the vertical space
    c.gridx = 0;
    c.gridy = 2;
    c. weightx = 1.0;
    c. weighty = 0.70;
    c.insets = new Insets (2,2,10,2);
    main_panel.add (hists_panel, c);

    // Add text area with scrolling to the content_pane.
    content_pane.add (main_panel);

  } // init

  /** Respond to the buttons. **/
  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fStartButton)  {
        if (fStartButton.getText ().equals ("Start") )
            start ();
        else
            stop ();
    } else if  (source == fClearButton) {
        fHistData.clear ();
        // For adaptable histogram, clear bins
        // and also clear internal data array.
        fHistChan.reset ();
        repaint ();

    } else if (!fInBrowser)// Exit button
        System.exit (0);
  } // actionPerformed

  /**
    * Make the connection to the server. Set up the DataReader
    * and begin recording the data from the server.
   **/
  public void start (){

    if (fConnected) stop ();

    // Clear the histograms
    fHistData.clear ();
    fHistData.clear ();

    // Get the current values of the host IP address and
    // and the username
    fHost = fHostField.getText ();
    fUserName = fUserNameField.getText ();
    try {
       fChannelToMonitor = Integer.parseInt (fChanField.getText ());
    }
    catch (NumberFormatException ex) {
      println ("Bad channel value");
      return;
    }

    // Now try to connect to the DataServer
    try{
      if (connect () ) {
         // Successful so set flags and change button text
         fConnected = true;
         fStartButton.setText ("Stop");
         fStatusLabel.setText ("Connected");
         fStatusLabel.setForeground (Color.BLUE);

      } else {
        println ("* NOT CONNECTED *");
        fStatusLabel.setText ("Disconnected");
        fStatusLabel.setForeground (Color.RED);
      }
    }
    catch (IOException e) {
        println ("* NOT CONNECTED *");
        fStatusLabel.setText ("Disconnected");
        fStatusLabel.setForeground (Color.RED);
    }
  } // start

  /**
    *  Connect to the server via a socket. Throws IOException
    *  if socket connection fails.
   **/
  boolean connect () throws IOException {

    println ("Attempting to connect to server ...");

    try {
       // Connect to the server using the host IP address
       // and the port at the server location
       fServer = new Socket (fHost, fDataServerPort);
    } catch (SecurityException se) {
       println ("Security Exception:\n"+se);
       return false;
    }

    println ("Server connected - create worker");

    // Create the worker to tend to this server
    fDataClientWorker =
      new DataClientWorker (this, fServer, fUserName);
    fDataClientWorker.start ();

    return true;
  } // connect

  /** Stop the worker thread. **/
  public void stop () {

    // Disconnect and kill the fDataClientWorker thread
    fDataClientWorker.finish ();
    setDisconnected ();
  }

   /** Set buttons for restart. **/
  void setDisconnected () {
    fStartButton.setText ("Start");
    fStatusLabel.setText ("Disconnected");
    fStatusLabel.setForeground (Color.RED);
  }

  /**
    *  The DataClientWorder passes the data array from the server.
    *  Display the data set by packing a histogram.
    *
    *  Also, plot the distribution of one of the channels of the data.
    *  The channel number is given in the text field.
   **/
   void setData (int [] data) {

      // Display each data set
      fHistData.pack (data, 0, 0, 0.0, (double) (data.length) );
      fHistDataPanel.getScaling ();

      // Plot the distribution of one of the channels in the data.
      if (fChannelToMonitor >= 0 && fChannelToMonitor < data.length) {
          fHistChan.setTitle ("Channel "+ fChannelToMonitor);
          fHistChan.add ((double)data[fChannelToMonitor]);
          // Adapt since data varies from channel to channel.
          fHistChan.rebin ();
          // Now rescale and draw.
          fHistChanPanel.getScaling ();
      }
      repaint ();
   } // setData


  /**  Convenience method for sending messages to the  text area. **/
  public void println (String str) {
     fMessageArea.append (str +"\n");
     repaint ();
  }

  /** Create the applet and add to frame. **/
  public static void main (String[] args) {
    //
    int frame_width=500;
    int frame_height=650;

    // Create standalone version
    DataClient applet = new DataClient ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Data Clent");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.getContentPane ().add ( applet);
    f.setSize (new Dimension (frame_width,frame_height));
    f.setVisible (true);
  } // main

} // class DataClient

package DataMonitor;

import java.io.*;
import java.net.*;

/**
  * DataClient creates an instance of this thread class to tend to the
  * connectionto the data server.
  *
  * It periodically requests a set of data from the server and then
  * sends the data to the parent DataClient, which will update its
  * display.
 **/
public class DataClientWorker extends Thread
{
  // Flag for data running.
  boolean fKeepRunning = true;

  DataClient fDataClient;

  // Networking refs
  Socket fServer;
  String fUserName;

  // I/O with the server
  BufferedReader  fNetInputReader ;
  DataInputStream fNetInputDataStream;
  OutputStream    fNetOutputDataStream ;
  PrintWriter     fPrintWriter;

  // Number of data values
  int fNumChannels = -1;
  // Data array
  int [] fData = null;


  /** Receive the DataClient to provide with the data from
    * the server.
   **/
  public DataClientWorker (DataClient c, Socket server, String userName) {
    fDataClient = c;
    fServer     = server;
    fUserName   = userName;
  }

  /** Remain in a loop to monitor the I/O from the server.
    * Display the data.
   **/
  public void run () {

    // The socket connection was made by the caller, now
    // set up the streams and do a login
    try {
        if (!doConnection ()){
            fDataClient.println ("  Connection/login failed");
            return;
        }
    }
    catch (IOException ioe) {
      fDataClient.println ("  I/O exception with serve:"+ ioe);
    }

    int num_channels = -1;

    // This loops until either the connection is broken or the
    //stop button or stop key is hit
    while  (fKeepRunning) {

       // Ask the server to send data.
      try {
          writeNetOutputLine ("  send data");
      }
      catch  (IOException e){
        break;
      }

       // First number sent from server is an integer that gives
       // the number of data values to be sent.
      try {
          num_channels = readNetInputInt ();
      }
      catch  (IOException e) {
        break;
      }

      if (num_channels != fNumChannels) {
          fNumChannels = num_channels;
          fDataClient.println ("  Number data channels = "
                               + fNumChannels);
      }

      if (fNumChannels < 1){
         fDataClient.println ("  no data");
         break;
      }

      // Create an array to hold the data if not available
      if (fData == null || fNumChannels != fData.length)
          fData = new int[fNumChannels];


      for (int i=0; i < fNumChannels; i++) {
        try {
          fData[i] = readNetInputInt ();
          // Pass the data to the parent program
          fDataClient.setData (fData);
        }
        catch  (IOException e) {
          fDataClient.println ("IO Exception while reading data");
          break;
        }
      }

      // Ask for data every TimeUpdate
      try {
        Thread.sleep (fDataClient.fTimeUpdate);
      }
      catch (InterruptedException e)
      {}

    }

    if  (fServer != null) closeServer ();
    fDataClient.println ("disconnected");
    fDataClient.setDisconnected ();

  } // run

  /** Set up the streams with the server and then login. **/
  boolean doConnection () throws IOException {

    // Get the input and output streams from the socket
    InputStream in = fServer.getInputStream ();

    // Use the reader for obtaining text
    fNetInputReader = new BufferedReader (
        new InputStreamReader (in)) ;

    // User the DataInputStream for getting numerical values.
    fNetInputDataStream = new DataInputStream ( in );

    // Output stream for sending messages to the server.
    fNetOutputDataStream = fServer.getOutputStream ();

    // Write with a PrintWriter for sending text to the server.
    fPrintWriter= new PrintWriter (
      new OutputStreamWriter (fNetOutputDataStream, "8859_1"), true );

    // Now try the login procedure.
    if (!login ()) return false;

    return true;
  } // doConnection

  /** Here is a homemade login protocol. A password could
    * easily be added.
   **/
  boolean login () {

    fDataClient.println ("Waiting for login prompt...");

    String msg_line=readNetInputLine ();
    if (msg_line == null) return false;
    fDataClient.println (msg_line);
    if (!msg_line.startsWith ("Username:")) return false;

    fDataClient.println ("Send username " + fUserName);
    try {
      writeNetOutputLine (fUserName);
    }
    catch IOException e) {
      return false;
    }
    catch (Exception e) {
      fDataClient.println ("Error occurred in sending username!");
      return false;
    }

    fDataClient.println ("Waiting for response...");

    msg_line=readNetInputLine ();
    if (msg_line == null) return false;
    fDataClient.println (msg_line);

    return true;
  } // login

  /** Do all of the steps needed to stop the connection. **/
  public void finish (){

     // Kill the thread and stop the server
     fKeepRunning = false;
     closeServer ();
  }

  /** Close the socket to the server. **/
  void closeServer () {
    if (fServer == null) return;
    try {
      fServer.close ();
      fServer = null;
    }
    catch (IOException e)
    {}
  }

  /**
    * The net input stream is wrappped in a DataInputStream
    * so we can use readLine, readInt and readFloat
   **/
  String readNetInputLine () {
    try {
      return fNetInputReader.readLine ();
    }
    catch (IOException e) {
      return null;
    }
  }

  /** Read an integer value from the socket stream **/
  int readNetInputInt () throws IOException {
        return fNetInputDataStream.readInt ();
  }

  /**  Read float value from the socket stream. **/
  float readNetInputFloat () throws IOException {
        return fNetInputDataStream.readFloat ();
  }

  /**
    * The net output is a PrintWriter class which doesn't throw
    * IOException itself. Instead we have to use the PrintWriter
    * checkError () method and throw an exception ourselves if there
    * was an output error.
   **/
  void writeNetOutputLine (String string) throws IOException {

    fPrintWriter.println (string);
    if (fPrintWriter.checkError ()) throw (new IOException ());

    fPrintWriter.flush ();
    if (fPrintWriter.checkError ()) throw (new IOException ());
  } // writeNetOutputLine

} // class DataClientWorker
  

 

 

Last update: Dec. 11, 2004

  
  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.