/* * DataServer.java * * Created on 17 luty 2008, 08:47 * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package socketserver; /** * * @author */ import java.net.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; public class DataServer extends JFrame implements ActionListener, Runnable { // GUI setup attributes JMenuItem fMenuClose = null; JTextArea fTextArea = null; JTextField fTextField = null; JButton fStartButton = null; // Networking setup // Use a Vector to keep track of the DataWorker list. Vector fWorkerList = null; // Connect to clients ServerSocket fServerSocket = null; int fDataServerPort = 2222;// Default port number // Client counting and limit static int fClientCounter__ = 0; static int fMaxClients__ = 10; // Number of data values per set or "event" static int fNumDataVals__ = 6; // Flag for the server socket loop. boolean fKeepServing = true; /** Open frame for the user interface. **/ public static void main (String [] args) { // Can pass frame title in command line arguments String title="DataServer"; if (args.length != 0) title = args[0]; DataServer f = new DataServer (title); f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); f.setVisible (true); } // main /** * Pass a title to the frame via the constructor * argument. Build the GUI. **/ DataServer (String title) { super (title); // Create the vector to list the DataWorker objects fWorkerList = new Vector (); // Now build the GUI. Container content_pane = getContentPane (); // Create a user interface. content_pane.setLayout ( new BorderLayout () ); fTextArea = new JTextArea (""); JScrollPane area_scroll_pane = new JScrollPane (fTextArea); content_pane.add ( area_scroll_pane, "Center"); // Create a panel with a textfield and two buttons fTextField = new JTextField ("2222"); fStartButton = new JButton ("Start"); fStartButton.addActionListener (this); JPanel panel = new JPanel (new GridLayout (1,2)); JPanel button_panel = new JPanel (); panel.add (fTextField); button_panel.add (fStartButton); panel.add (button_panel); content_pane.add ( panel, "South"); // Use the helper method makeMenuItem // for making the menu items and registering // their listener. JMenu m = new JMenu ("File"); // Use File menu to hold Quit command. m.add (fMenuClose = makeMenuItem ("Quit")); JMenuBar mb = new JMenuBar (); mb.add (m); setJMenuBar (mb); setSize (400,400); } // ctor /** Process events from the frame menu and the chooser. **/ public void actionPerformed (ActionEvent e) { boolean status = false; String command = e.getActionCommand (); if (command.equals ("Start") ) { try { fDataServerPort = Integer.parseInt (fTextField.getText ()); }catch (NumberFormatException nfe){ println ("Bad port number"); return; } fStartButton.setEnabled (false); Thread thread = new Thread (this); thread.start (); } else if (command.equals ("Quit") ){ fKeepServing = false; dispose (); } } // actionPerformed /** Create a ServerSocket and loop waiting for clients. **/ public void run () { // The server_socket is used to make connections to // DataClients at this port number try { fServerSocket = new ServerSocket (fDataServerPort); } catch (IOException e) { println ("Error in server socket"); return; } println ("Waiting for users..."); // Loop here to grab clients while (fKeepServing) { try { // accept () blocks until a connection is made Socket socket = fServerSocket.accept (); // Do the setup this socket and then loop // back around to wait for the next DataClient. DataWorker worker = new DataWorker (this, socket); worker.start (); } catch (IOException ioe) { println ("IOException: <" + ioe + ">"); break; } catch (Exception e) { println ("Exception: <" + e + ">"); break; } } } // run /** * When a DataWorker makes the connection, it checks to see if * there is room on the server for it. * We synchronize the method to avoid any problems with multiple * clients interfering with each other. **/ public synchronized boolean clientPermit () { if (fWorkerList.size () < fMaxClients__) return true; else return false; } /** * A DataWorker will set up the connection with the client. If it * decides that the conditions are OK, then it will invoke this * method so that the parent server will add the worker to its * list. * We synchronize the method to avoid any problems with multiple * clients interfering with each other. **/ public synchronized void clientConnected (DataWorker worker) { fWorkerList.add (worker); fClientCounter__++; } /** * When a client disconnects, the DataWorker object will * call back to this method to remove itself from the list * of workers. * We synchronize the method to avoid any problems with multiple * clients interfering with each other. **/ public synchronized void clientDisconnected (String user, DataWorker worker) { println ("Client: "+user+" disconneced"); fWorkerList.remove (worker); fClientCounter__--; } /** * This "helper method" makes a menu item and then * registers this object as a listener to it. **/ private JMenuItem makeMenuItem (String name){ JMenuItem m = new JMenuItem ( name ); m.addActionListener ( this ); return m; } /** Utility method to send messages to the text area. **/ public void println (String str){ fTextArea.append (str +"\n"); repaint (); } } // class DataServer
/* * DataWorker.java * * Created on 17 luty 2008, 08:49 * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package socketserver; import java.io.*; import java.net.*; import java.util.*; /** * DataServer uses an instance of this class to service * a DataClient connection. It waits for a data request * and then obtains and sends the data. * * In actual use, the data could, for example be obtained from a * diskfile updated by a DAQ system. Here, though, we simulate data * taking by generating an array of random numberfDataServer. **/ public class DataWorker extends Thread { // The parent server. DataServer fDataServer; // Name of the client String fUser; // Connection to the client. Socket fSocket; // I/O streams to the client InputStream fNetInputStream; OutputStream fNetOutputStream; // Wrapped I/O streams BufferedReader fNetInputReader; DataOutputStream fDataOutputStream; PrintWriter fPrintWriter; boolean fKeepRunning = true ; // Data production: // Smear each data channel with the following std.dev // for the Gaussian. double [] fStdDev = {10.0, 20.0, 30.0, 40.0, 50.0, 60.0}; Random fRan; /** * Pass reference to the owner DataServer and the * index number for this DataSender instance. **/ public DataWorker (DataServer s, Socket socket) { fDataServer = s; fSocket = socket; // Random number generator need for dummy data creation. fRan = new Random (); } /** Send data to the client. **/ public void run () { // If setup fails, end thread processing. if (!serviceSetup ()) return; fDataServer.println ( "Client connection and login OK - Begin service..."); // Lower tpriority to give main parent and // other threads some processor time. setPriority (MIN_PRIORITY); String client_last_msg = ""; // Begin the loop for communicating with the client. while (fKeepRunning){ // Read a request from the DataClient String client_msg = readNetInputLine (); if (client_msg == null ) break; // Only print message if it changes. Avoids printing same // message for each data set. if ( !client_msg.equals (client_last_msg)) fDataServer.println ("Message from " + fUser + ": " + client_msg); client_last_msg = client_msg; // Could interpret the request and do something accordingly // but here we will just send a set of data values. // Send the number of data values. try { writeNetOutputInt (DataServer.fNumDataVals__); } catch (IOException e) { break; } // Creat dummy data values and send them to the DataClient. for (int i=0; i< DataServer.fNumDataVals__; i++){ // Select that range of Gaussian widths for the data // values for each channel of the data set. Add an offset // to get most negative values above zero. int i_std_dev = i%6; double dat = 3.0*fStdDev[i_std_dev] + fStdDev[i_std_dev] * fRan.nextGaussian (); if (dat < 0.0) dat = 0.0; // Pass only integer values; int idat = (int) dat; try { writeNetOutputInt (idat); } catch (IOException e) { break; } } } // Send message back to the text area in the frame. fDataServer.println (fUser + " has disconnected."); // Do any other tasks for ending the worker. signOff (); } // run /** * Set up the connection to the client. This requires obtaining the * IO streams, carrying out the login prototcol, and then starting * a DataWorker thread to tend to the client. * * The bookkeeping code is a bit messy because we check both reads * and writes for errors in case the connection breaks down. * * The reads catch their own IOExceptions and return a null, while * string writes use a PrintWriter that doesn't throw IOException. So * we use the checkError () method and throw it ourselvefDataServer. **/ public boolean serviceSetup () { fDataServer.println ("Client setup..."); // First get the in/out streams from the socket to the client try{ fNetInputStream = fSocket.getInputStream (); fNetOutputStream = fSocket.getOutputStream (); } catch (IOException e){ fDataServer.println ("Unable to get input/output streams"); return false; } // Create a PrintWriter class for sending text to the client. // The writeNetOutputLine method will use this class. try{ fPrintWriter = new PrintWriter ( new OutputStreamWriter (fNetOutputStream, "8859_1"), true ); } catch (Exception e) { fDataServer.println ("Fails to open PrintWriter to client!"); return false; } // Check if the server has room for this client. // If not, then send a message to this client to tell it // the bad news. if ( !fDataServer.clientPermit () ) { try{ String msg= "Sorry, We've reached maximum of clients"; writeNetOutputLine (msg); fDataServer.println (msg); return false; } catch (IOException e){ fDataServer.println ("Connection fails during login"); return false; } } // Get a DataInputStream wrapper so we can use its // readLine () methods. fNetInputReader = new BufferedReader (new InputStreamReader (fNetInputStream)); // Do a simple login protocol. Send a request for the users name. // Note a password check could be added here. try{ writeNetOutputLine ( "Username: "); } catch (IOException e){ fDataServer.println ("Connection fails during login"); return false; } // Read the user name. fUser = readNetInputLine (); if (fUser == null ) { fDataServer.println ("Connection fails during login"); return false; } // Send a message that the login is OK. try{ writeNetOutputLine ("Login successful"); fDataServer.println ("Login successful for " + fUser); } catch (IOException e){ fDataServer.println ("Connection fails during login for " + fUser); return false; } fDataServer.println (fUser + " connected! "); fDataServer.println (fSocket.toString ()); // The login is successful so now create a DataWorker to // service this client. Pass it an ID number fDataServer.clientConnected (this); // Get a data output stream for writing numerical data to the client fDataOutputStream = new DataOutputStream (fNetOutputStream); return true; } // serviceSetup /** Whenever this client disconnects tell the parent. **/ public void signOff () { try{ fSocket.close (); }catch (Exception e){ fDataServer.println ("Socket close exception for " + fUser); } fDataServer.clientDisconnected (fUser,this); } // signOff /** Utility method to read a whole text line.**/ String readNetInputLine () { try { return fNetInputReader.readLine (); } catch (IOException e){ return null; } } // readNetInputLine /** * Output is wrapped with a PrintWriter, which doesn't throw * IOException. So we invoke the checkError() method and then * throw an exception if it detects an 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 /** Utility to write integer values to the output stream. **/ void writeNetOutputInt (int i) throws IOException { fDataOutputStream.writeInt (i); fDataOutputStream.flush (); } // writeNetOutputInt /** Utility to write float values to the output stream.**/ void writeNetOutputFloat (float f) throws IOException { fDataOutputStream.writeFloat (f); fDataOutputStream.flush (); } // writeNetOutputFloat } // class DataWorker