NetRatServer Implementation Final Iteration


NetRatServer Implementation — Fourth Iteration

There is only one piece of the robot rat server application design left to implement and that is the ability for it to handle multiple socket-based client connections. To do this it must be a multi-threaded server. Also, since socket-based communication takes place at a lower level than RMI method calls, a client-server protocol must be established to facilitate client-server communication. Table 20-7 presents the design considerations and decisions that apply to the robot rat server application fourth iteration development activities.

Table 20-7: Third Iteration Design Considerations and Decisions

Check-Off

Design Consideration

Design Decision

 

Objectives for fourth iteration server application development activities.

The objective of the fourth iteration development activities is to modify the robot rat server application so it can process multiple socket-based client connections. To do this the ThreadedClientProcessor class must be created. The NetRatServer class must also be modified to listen for incoming socket-based client connections and pass these connections to an instance of ThreadedClientProcessor for further processing.

 

ThreadedClientProcessor class

The ThreadedClientProcessor class will extend the Thread Class. Its responsibility will be to process client communication for as long as the client application is connected to the server. It will take an instance of java.net.Socket as an argument to its constructor. The Socket object will be used to obtain the InputStream and Output-Stream objects through which client-server communication will occur. The ThreadedClientProcessor class is also responsible for translating client data into robot rat movement commands.

 

NetRatServer class

The NetRatServer class must be modified to listen for incoming socket-based client connections. When an incoming client connections is detected the resulting Socket object will be passed off to an instance of ThreadedClientProcessor for continued processing.

 

client-server protocol

A simple protocol must be established that allows data to be sent from the client application and translated into robot rat commands. The requirements of the robot rat application are simple. The client will send the integer values 0 through 7 to the server in response to user button clicks. The integer values will be mapped in the ThreadedClientProcessor to RobotRat movement method calls according the following mapping:

NORTH = 0;

NORTH_EAST = 1;

EAST = 2;

SOUTH_EAST = 3;

SOUTH = 4;

SOUTH_WEST = 5;

WEST = 6;

NORTH_WEST = 7;

Fourth Iteration Code

The following new and modified code is the result of fourth iteration development activities.

Referring to examples 20.15 and 20.16 — the ThreadedClientProcessor class takes a Socket reference as an argument to its constructor. The Socket reference is used to obtain the InputStream and OutputStream references. The OutputStream reference is used on line 24 to create the DataOutputStream object and the InputStream reference is used on line 25 to create the DataInputStream object. If lines 24 and 25 execute normally then line 26 will execute creating the instance of RobotRat that will be moved about the screen during this client session.

Example 20.15: ThreadedClientProcessor.java

image from book
 1     import java.io.*; 2     import java.net.*; 3 4     public class ThreadedClientProcessor extends Thread { 5 6         private Socket                 _socket    = null; 7         private DataInputStream        _dis       = null; 8         private DataOutputStream       _dos       = null; 9         private RobotRatInterface      _robot_rat = null; 10 11        private static final int NORTH      = 0; 12        private static final int NORTH_EAST = 1; 13        private static final int EAST       = 2; 14        private static final int SOUTH_EAST = 3; 15        private static final int SOUTH      = 4; 16        private static final int SOUTH_WEST = 5; 17        private static final int WEST       = 6; 18        private static final int NORTH_WEST = 7; 19 20 21       public ThreadedClientProcessor(Socket socket){ 22         _socket = socket; 23         try{ 24          _dos = new DataOutputStream(_socket.getOutputStream()); 25          _dis = new DataInputStream(_socket.getInputStream()); 26          _robot_rat = new RobotRat(); 27         }catch(Exception e){ 28            System.out.println("ThreadedClientProcessor: Problem creating IOStream objects!"); 29 30            e.printStackTrace(); 31         } 32       }// end constructor 33 34 35       public void run(){ 36         int command = 0; 37         try{ 38           while((command = _dis.readInt()) != -1){ 39             switch(command){ 40               case NORTH       : _robot_rat.moveNorth(); 41                                  _dos.writeUTF("Rat moved North"); 42                                  break; 43               case NORTH_EAST  : _robot_rat.moveNorthEast(); 44                                  _dos.writeUTF("Rat moved North-East"); 45                                  break; 46               case EAST        : _robot_rat.moveEast(); 47                                  _dos.writeUTF("Rat moved East"); 48                                  break; 49               case SOUTH_EAST  : _robot_rat.moveSouthEast(); 50                                  _dos.writeUTF("Rat moved South-East"); 51                                  break; 52               case SOUTH       : _robot_rat.moveSouth(); 53                                  _dos.writeUTF("Rat moved South"); 54                                  break; 55               case SOUTH_WEST  : _robot_rat.moveSouthWest(); 56                                  _dos.writeUTF("Rat moved South-West"); 57                                  break; 58               case WEST        : _robot_rat.moveWest(); 59                                  _dos.writeUTF("Rat moved West"); 60                                  break; 61               case NORTH_WEST  : _robot_rat.moveNorthWest(); 62                                  _dos.writeUTF("Rat moved North-West"); 63                                  break; 64               default          : _dos.writeUTF("Invalid command"); 65 66 67             }// end switch 68           }// end while 69         }catch(EOFException ignored){ } 70         catch(Exception e){ 71           e.printStackTrace(); 72          }  73          finally{ 74            try{ 75            _socket.close(); 76             }catch(Exception ignored){ } 77          } 78 79          try{ 80            _socket.close(); 81            }catch(Exception ignored){ } 82      } // end run() 83    }// end ThreadedClientProcessor class definition
image from book

Example 20.16: NetRatServer.java (mod 3)

image from book
 1     import javax.swing.*; 2     import java.awt.*; 3     import java.awt.event.*; 4     import java.rmi.*; 5     import java.rmi.registry.*; 6     import java.net.*; 7 8     public class NetRatServer extends JFrame implements ActionListener { 9 10      private JButton button1 = null; 11      private RobotRatInterface _r1 = null; 12      private RobotRatInterface _r2 = null; 13 14      public NetRatServer(){ 15        try{ 16        _r1 = new RobotRat(); 17        _r2 = new RobotRat(); 18        }catch(Exception ignored){ ignored.printStackTrace(); } 19        button1 =  new JButton("move"); 20        button1.addActionListener(this); 21        this.getContentPane().add(button1); 22        this.setSize(100, 100); 23        this.setLocation(300, 300); 24        this.show(); 25      } 26 27      public void actionPerformed(ActionEvent ae){ 28        if(ae.getActionCommand().equals("move")){ 29          try{ 30          _r1.moveEast(); 31          _r2.moveSouthEast(); 32          }catch(Exception ignored){ } 33        } 34      } 35 36      public static void main(String[] args){ 37        System.out.println("NetRatServer Lives!!"); 38        new NetRatServer(); 39        try{ 40        System.out.println("Starting registry..."); 41        LocateRegistry.createRegistry(1099); 42        System.out.println("Registry started on port 1099."); 43        System.out.println("Binding service name to remote object..."); 44        Naming.bind("Robot_Rat_Factory", new RobotRatFactory()); 45        System.out.println("Bind successful."); 46        System.out.println("Ready for remote method invocation."); 47 48        System.out.println("Creating ServerSocket Object..."); 49        ServerSocket server_socket = new ServerSocket(5001); 50        System.out.println("ServerSocket object created successfully!"); 51        while(true){ 52          System.out.println("Listening for incoming client connections..."); 53          Socket socket = server_socket.accept(); 54          System.out.println("Incoming client connection detected."); 55          System.out.println("Creating ThreadedClientProcessor object..."); 56          ThreadedClientProcessor client_processor = new ThreadedClientProcessor(socket); 57          System.out.println("ThreadedClientProcessor object created."); 58          System.out.println("Calling start() method..."); 59          client_processor.start(); 60         } 61        }catch(Exception e){ 62          e.printStackTrace(); 63        } 64      } 65    }// end NetRatServer class
image from book

The bulk of the processing in the ThreadedClientProcessor class takes place in the body of the run() method. Client commands are read with the DataInputStream reference _dis. Client commands are translated into RobotRat move commands in the body of the switch statement according to the established protocol. In response to each client command the ThreadedClientProcessor will respond to the client with a UTF string indicating which way the RobotRat moved.

Turn your attention now to the modified NetRatServer class. Additional code was added starting on line 48 that enables the NetRatServer application to listen for incoming client connections using a ServerSocket object. In this example the server will listen for incoming client connections on port 5001. The while statement starting on line 51 performs the bulk of the processing, continuously listening for incoming client connections and passing the resulting Socket reference to the constructor of a ThreadedClientProcessor object and then calling its start() method.

Testing Fourth Iteration Code

To fully test the latest version of the NetRatServer application you’ll need to code up a socket-based client application. Example 20.17 gives the code for the Socket_NetRatClient class that allows you to connect to the robot rat server application using sockets.

Example 20.17: Socket_NetRatClient.java

image from book
 1     import java.net.*; 2     import java.io.*; 3     import java.awt.*; 4     import java.awt.event.*; 5     import javax.swing.*; 6 7     public class Socket_NetRatClient extends JFrame implements ActionListener { 8 9       private JButton _button1 = null; 10      private JButton _button2 = null; 11      private JButton _button3 = null; 12      private JButton _button4 = null; 13      private JButton _button5 = null; 14      private JButton _button6 = null; 15      private JButton _button7 = null; 16      private JButton _button8 = null; 17      private JButton _button9 = null; 18      private Socket  _socket  = null; 19      private DataInputStream  _dis = null; 20      private DataOutputStream _dos = null; 21 22      private static final int NORTH      = 0; 23      private static final int NORTH_EAST = 1; 24      private static final int EAST       = 2; 25      private static final int SOUTH_EAST = 3; 26      private static final int SOUTH      = 4; 27      private static final int SOUTH_WEST = 5; 28      private static final int WEST       = 6; 29      private static final int NORTH_WEST = 7; 30 31 32      public Socket_NetRatClient(String host){ 33        super("Robot Rat Control Panel"); 34 35      try{ 36         _socket = new Socket(host, 5001); 37         _dos = new DataOutputStream(_socket.getOutputStream()); 38         _dis = new DataInputStream(_socket.getInputStream()); 39         }catch(Exception e){ 40          e.printStackTrace(); 41         } 42        this.setUpGui(); 43      } 44 45      public void actionPerformed(ActionEvent ae){ 46       try{ 47       if(ae.getActionCommand().equals("N")){ 48           _dos.writeInt(NORTH); 49           System.out.println(_dis.readUTF()); 50       } else if(ae.getActionCommand().equals("NE")){ 51           _dos.writeInt(NORTH_EAST); 52           System.out.println(_dis.readUTF()); 53       } else if(ae.getActionCommand().equals("E")){ 54           _dos.writeInt(EAST); 55           System.out.println(_dis.readUTF()); 56       } else if(ae.getActionCommand().equals("SE")){ 57           _dos.writeInt(SOUTH_EAST); 58           System.out.println(_dis.readUTF()); 59       } else if(ae.getActionCommand().equals("S")){ 60           _dos.writeInt(SOUTH); 61           System.out.println(_dis.readUTF()); 62       } else if(ae.getActionCommand().equals("SW")){ 63           _dos.writeInt(SOUTH_WEST); 64           System.out.println(_dis.readUTF()); 65       } else if(ae.getActionCommand().equals("W")){ 66           _dos.writeInt(WEST); 67           System.out.println(_dis.readUTF()); 68       } else if(ae.getActionCommand().equals("NW")){ 69           _dos.writeInt(NORTH_WEST); 70           System.out.println(_dis.readUTF()); 71       } 72 73       }catch(Exception e){ 74         System.out.println("actionPerformed(): problem with socket IO."); 75         e.printStackTrace(); 76        } 77      } 78 79      public void setUpGui(){ 80       _button1 = new JButton("NW"); 81       _button1.addActionListener(this); 82 83       _button2 = new JButton("N"); 84       _button2.addActionListener(this); 85 86       _button3 = new JButton("NE"); 87       _button3.addActionListener(this); 88 89       _button4 = new JButton("W"); 90       _button4.addActionListener(this); 91 92       _button5 = new JButton(""); 93       _button5.addActionListener(this); 94 95       _button6 = new JButton("E"); 96       _button6.addActionListener(this); 97 98       _button7 = new JButton("SW"); 99       _button7.addActionListener(this); 100 101      _button8 = new JButton("S"); 102      _button8.addActionListener(this); 103 104      _button9 = new JButton("SE"); 105      _button9.addActionListener(this); 106 107      this.getContentPane().setLayout(new GridLayout(3,3,0,0)); 108      this.getContentPane().add(_button1); 109      this.getContentPane().add(_button2); 110      this.getContentPane().add(_button3); 111      this.getContentPane().add(_button4); 112      this.getContentPane().add(_button5); 113      this.getContentPane().add(_button6); 114      this.getContentPane().add(_button7); 115      this.getContentPane().add(_button8); 116      this.getContentPane().add(_button9); 117      this.setSize(200, 200); 118      this.setLocation(300, 300); 119      this.pack(); 120      this.show(); 121     } // end setUpGui() method 122 123     public static void main(String[] args){ 124        try{ 125          new Socket_NetRatClient(args[0]); 126        }catch(ArrayIndexOutOfBoundsException e1){ 127           System.out.println("Usage: java NetRatClient <host>"); 128         } 129         catch(Exception e2){ 130           e2.printStackTrace(); 131         } 132     }// end main() method 133   }// end Socket_NetRatClient class definition
image from book

Referring to example 20.17 —the code for the Socket_NetRatClient class looks very similar to its RMI_NetRatClient cousin. The similarities between the two lay mostly with the look-and-feel of the user interface. Users send commands to robot rats using a 3 × 3 grid of buttons. However, in this version of the client, commands are sent to the server via a Socket connection using the DataOutputStream class. Server responses are read using the DataInputStream class.

The Socket connection is established in the body of the constructor. The host is specified on the command line when the client application is launched. The bulk of the processing takes place in the body of the actionPerformed() method starting on line 45. In response to directional button clicks the corresponding command is sent to the server. The server response is immediately expected and processed. In this case the server response string is simply printed to the console.

With the socket-based client application complete you are now ready to test the results of the fourth iteration development activities.

Testing The Fourth Iteration Code

You can now have some real fun with the robot rat application. Crank up the NetRatServer then launch several Socket_NetRatClient and RMI_NetRatClient applications. Test from the local machine and from a remote computer if you have one available.

I will not step through the testing of this iteration as I did for the previous one. Socket-based client robot rats look and feel the same on the floor as their RMI-controlled counterparts. There are, however, a few loose ends that need to be addressed before the NetRatServer application can be called complete. The primary issue at hand is that when clients disconnect from the server their corresponding rat images remain on the floor. It would be nice if they would disappear when the client disconnected.

Secondly, several “magic values” appear in the body of the server code. (i.e., the size of the floor’s JFrame and its location on the screen, the number of pixels the rat image moves, etc.) From a configuration standpoint magic values render a program difficult to maintain. A change in floor size, for example, would require the RobotRat class to be recompiled, its stub regenerated, and the code redistributed. For a large server application this requirement alone would be a show-stopper.

Finally, code that appears in the application for testing purposes can be removed. This primarily applies to the two instances of RobotRat that are created in the NetRatServer application. These issues are addressed in one last implementation iteration.

Quick Review

Programming a socket-based client-server application requires attention to lower-level details when compared with RMI-based applications. To handle multiple socket-based client connections, a socket-based server application must be multi-threaded. The socket-based server must also establish an application protocol so the client application can effectively communicate with the server application.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net