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

NetRand Project

*

Software Engineering - CS536

*

University of Wisconsin - Milwaukee

*

Authors:

* *
File: RandomByteServer.java *
*

* The RandomByteServer handles TCP connections requesting a sequence of random * bytes from the BitPool. When involked from the command line, the * RandomByteServer accesses the ServerPrefs * class to obtain the parameters for initializing the server. The server * calls the BitPool class to handle the obtaining * and storage of the random bytes. When the RandomByteServer gets a request * over the socket, it gets the requested bytes from the BitPool class and * sends then over the accepted TCP socket. */ public class RandomByteServer extends Thread { // --------- Class Variables ---------- // /** accepts requests for random bytes */ private ServerSocket theServer; /** port to listen for byte requests */ private int serverPort; /** how many TCP connections to queue on the socket */ private int serverQueueLength; /** flag for if the request parameters are set */ private boolean requestParamSet = false; /** handles the collection and storage of random bytes */ private BitPool entropy; /** port BitPool class should listen for client's data packets */ private int collectionPort; /** maximum size of the byte pool */ private int maxPoolSize; /** maximum number of bytes a user can request at one time */ private int maxRequestSize; /** flag for if the BitPool's parameters were set */ private boolean poolParamSet = false; // ---------- Class Methods ---------- // /** * Run when the RandomByteServer is involked from the command line. * Creates a RandomByteServer thread and calls start() */ public static void main( String[] args ) { try { ServerPrefs prefs = new ServerPrefs(); RandomByteServer server = new RandomByteServer(prefs); server.start(); } catch ( Exception e ) { System.err.println( e ); } } // ----------------------------------- // /** * constructor for the RandomByteServer class * @param prefs - ServerPrefs object * @exception ByteServerException if either the request socket or the * BitPool parameters were not properly set. */ public RandomByteServer( ServerPrefs prefs ) throws ByteServerException { if ( !setRequestSocketParam( prefs.reqPort, prefs.reqPortQueue ) ) throw new ByteServerException( "setRequestSocketParam() failed" ); if ( !setBitPoolParam( prefs.collPort, prefs.poolSize, prefs.reqSize ) ) throw new ByteServerException( "setBitPoolParam() failed" ); initRandomByteServer(); } // ----------------------------------- // /** * initializes the RandomByteServer, by calling initBitPool() and * initRequestServer() * @exception ByteServerException if either the request server or the * BitPool could not be initialized */ public void initRandomByteServer() throws ByteServerException { if ( !initBitPool() ) throw new ByteServerException( "could not intitialize BitPool" ); if ( !initRequestServer() ) throw new ByteServerException( "could not intitialized RequestServer" ); } // ----------------------------------- // /** * sets the parameters for the request server socket * @param reqPort - the port to listen for random byte requests * @param reqPortQueue - how many incoming TCP connections to queue * @return true iff all the parameters were properly set, otherwise false. */ private boolean setRequestSocketParam( int reqPort, int reqPortQueue ) { if ( reqPort < 1 || reqPort > 65535 ) { System.err.println( reqPort + ": invalid port number" ); return false; } else serverPort = reqPort; if ( reqPortQueue < 1 ) { System.err.println( reqPortQueue + ": socket queue length must be > 0" ); return false; } else serverQueueLength = reqPortQueue; requestParamSet = true; return true; } // -------------------------------------- // /** * sets the BitPool's parameters * @param collPort - the port to listen for client's data packets * @param poolSize - the maximum byte size of the BitPool's byte buffer * @param reqSize - the maximum number of bytes a user can request at on time * @return true iff all the parameters were properly set, otherwise false. */ private boolean setBitPoolParam( int collPort, int poolSize, int reqSize ) { if ( collPort < 1 || collPort > 65535 ) { System.err.println( collPort + ": invalid port number" ); return false; } else collectionPort = collPort; if ( poolSize < 1 ) { System.err.println( poolSize + ": invalid byte pool size" ); return false; } else maxPoolSize = poolSize; if ( reqSize > maxPoolSize ) { System.err.println( reqSize + ": max request must be <= max pool size = " + poolSize ); return false; } else maxRequestSize = reqSize; poolParamSet = true; return true; } // ------------------------------------- // /** * initializes the BitPool object, and starts the BitPool Thread * @return true iff the BitPool was properly initialized, otherwise false */ private boolean initBitPool() { if ( poolParamSet == false ) { System.err.println( "parameters for BitPool were not set" ); return false; } try { entropy = new BitPool(maxRequestSize, maxPoolSize, collectionPort); entropy.start(); } catch ( BitPoolException e ) { System.err.println( e ); System.exit(1); } return true; } // ------------------------------------ // /** * initializes the request server * @return true iff the request server was properly initialized, otherwise false */ private boolean initRequestServer() { if ( requestParamSet == false ) { System.err.println( "parameters for RequestServer were not set" ); return false; } try { theServer = new ServerSocket(serverPort, serverQueueLength); } catch ( IOException e ) { System.err.println( "RandomByteServer: ServerSocket( " + serverPort + ", " + serverQueueLength + " ) failed " ); System.exit(1); } return true; } /** * overrides Thread run() method, waites for request connections over the * TCP socket. When a request is received, the respondToRequest() function * is called to handle the request */ public void run() { Socket incoming = null; BufferedReader fromSocket = null; DataOutputStream toSocket = null; byte[] response = null; byte[] request = new byte[150]; while ( entropy.isAlive() ) { try { incoming = theServer.accept(); incoming.setSoTimeout(10000); fromSocket = new BufferedReader( new InputStreamReader(incoming.getInputStream()) ); toSocket = new DataOutputStream(incoming.getOutputStream()); try { response = respondToRequest( fromSocket.readLine() ); } catch ( InterruptedIOException ie ) { String err = new String( "connection timeout...\n\r" ); response = err.getBytes(); } toSocket.write(response,0,response.length); toSocket.flush(); incoming.close(); } catch ( Exception e ) { try { System.err.println( "Request socket failure: " + e ); System.err.println( "Attempting to reopen request socket" ); incoming.close(); theServer.close(); initRequestServer(); } catch ( IOException ie ) {} } } System.err.println( "BitPool thread died" ); } // ------------------------------------ // /** * handles a request for random bytes from the BitPool * @param request - string containing the number of bytes the user is requesting * @return an array of bytes containing the number of bytes the user requested, * or if the user's request was illegel, the byte array contains the appropriate * error message */ private byte[] respondToRequest( String request ) { int numBytes = 0; try { numBytes = Integer.parseInt(request); } catch ( NumberFormatException ne ) { String err = new String( request + ": invalid request\n\r" ); return err.getBytes(); } if ( numBytes < 1 || numBytes > maxRequestSize ) { String err = new String( numBytes + ": must be > 0 and < " + maxRequestSize + "\n\r" ); return err.getBytes(); } byte[] response; if ( entropy.hasBytesAvailable(numBytes) ) { try { response = entropy.getBytesFromPool(numBytes); return response; } catch ( BitPoolException bp ) { System.err.println( bp ); String err = new String( "failed to retrieve bytes...try again\n\r" ); return err.getBytes(); } } String err = new String( "out of random bytes...try again later\n\r" ); return err.getBytes(); } }