Project Specification


A Simple Socket-Based Client-Server Example

This section presents a simple socket-based client-server application. Before getting into the code let’s review the sequence of events that occur in the lifetime of a socket-based client-server application. The connection scenario starts with the client and server applications as shown in figure 20-1.

image from book
Figure 20-1: Client and Server Applications

The server application utilizes a ServerSocket object to listen for incoming client connections on a specified port. To connect to the server, the client application must be given the server’s URL or IP address and port number. The incoming client connection is illustrated in figure 20-2. When the incoming client connection is detected a Socket object is retrieved from the ServerSocket object as is shown in figure 20-3.

image from book
Figure 20-2: Incoming Client Connection

image from book
Figure 20-3: The Connection is Established — There are Sockets at Both Ends of the Connection

Once the connection is successfully established and the Socket objects are set up on both ends of the connection the Socket objects are used to retrieve the IOStream objects through which communication will take place. This concept is illustrated in figure 20-4. The IOStream objects (an InputStream and OutputStream) are used to create the required IOStream object suitable to the communication task at hand. For instance, if you intend to send serialized objects over the wire then you need to create an ObjectInputStream and an ObjectOutputStream. If you want to send Strings and primitive types then perhaps the DataInputStream and DataOutputStream classes will suffice.

image from book
Figure 20-4: The Socket Objects are Used to Retrieve the IOStream Objects

In multi-threaded server applications the client communication activities are handled in a separate thread.

When the client disconnects, the socket must be closed with a call to the Socket.close() method.

Now that we’ve reviewed the basic activities associated with a socket-based client-server application let’s take a look at some code that implements a simple socket-based client-server application. I’ll start with the server application.

Simple Server Application

Example 20.1 gives the code for an application class named SimpleServer. The SimpleServer application implements a simple socket-based single-threaded server application. It’s simple because all it does is echo message strings back to a client application. It’s single threaded because it can only process one client communication session at a time.

Example 20.1: SimpleServer.java

image from book
 1     import java.net.*; 2     import java.io.*; 3 4     public class SimpleServer { 5       public static void main(String[] args){ 6         try{ 7         System.out.println("Creating ServerSocket object binding to port 5001..."); 8         ServerSocket server_socket = new ServerSocket(5001); 9         boolean keep_going = true; 10         while(keep_going){ 11           System.out.println("Listening for incoming client connections on port 5001..."); 12           Socket socket = server_socket.accept(); 13           System.out.println("Incoming client connection detected..."); 14           System.out.println("Creating Input- and OutputStream objects..."); 15 16           DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); 17           DataInputStream dis = new DataInputStream(socket.getInputStream()); 18 19           String input_string = "Ready"; 20           System.out.println("Processing client String input..."); 21           try{ 22             while((input_string = dis.readUTF()) != null){ 23               System.out.println(input_string); 24               dos.writeUTF("Echo from server: " + input_string); 25 26               if(input_string.equals("disconnect client")){ 27                 System.out.println("Client disconnected..."); 28                 socket.close(); 29                 break; 30                 }else if(input_string.equals("shutdown server")){ 31                    socket.close(); 32                    keep_going = false; 33                    break; 34                   } 35                } // end inner while loop 36             }catch(EOFException ignored){ } 37            catch(Exception e1){ 38               e1.printStackTrace(); 39             } 40           } // end outer while loop 41           System.out.println("Shutting down server..."); 42          }catch(Exception e){ 43            e.printStackTrace(); 44           } 45        } // end main() 46       }// end SimpleServer class definition
image from book

Referring to example 20.1 — when the SimpleServer application starts up it creates a ServerSocket object on line 8 that is bound implicitly to the default host IP address and explicitly to port 5001. The rest of the application is then enclosed in a while loop that repeatedly executes a few basic server tasks. First, utilizing the ServerSocket object on line 12, it attempts to create a Socket object by calling the ServerSocket.accept() method. The accept() method blocks right there until it detects an incoming client connection. When an incoming client connection is detected the accept() method returns and the Socket object is created. Next, the DataOutputStream and DataInputStream objects are created by calling the Socket.getOutputStream() and Socket.getInputStream() methods respectively. Once the IOStream objects are created the server application is ready to communicate with the client.

Client communication is handled in the inner while loop that begins on line 22. The DataInputStream object is used to read a UTF string from the client using the readUTF() method. (The client must send a UTF string!) The string sent by the client is then written to the server console and immediately sent back to the client application.

Examine line 26 — if the input_string equals “disconnect client” the socket is closed and the inner while loop is exited with the break statement. The server application then returns to listening for incoming client connections and the process repeats when another client connection is detected. If, however, the input_string equals “shutdown server” then the socket is closed and the keep_going metaphor value is set to false and the outer while loop exits. This causes the SimpleServer application to exit.

Most of the code for the SimpleServer application is enclosed in a try-catch block. When dealing with network programming issues and IOStream communication a multitude of exceptions must be addressed and properly handled. One exception that is specifically addressed in the SimpleServer example is the EOFException that will result if a client connection is suddenly dropped. The EOFException is thrown by the DataInputStream.readUTF() method. (In addition to IOException and UTFDataFormatException). Otherwise, the SimpleServer application simply tries to recover somewhat gracefully from the myriad exceptions the can possibly be thrown during the several stages of the server life cycle. A robust server application must do everything in its power to properly handle exceptional conditions.

Now that you’ve seen the SimpleServer application code let’s move on to see the code for a simple client application that can connect to a running instance of SimpleServer and exchange messages with it.

Simple Client Application

Example 20.2 provides the source code for an application class named SimpleClient. The SimpleClient application provides a basic GUI front-end to a client application that can connect to and exchange messages with a running instance of SimpleServer. Read through the code and at the end I’ll discuss some of its important features.

Example 20.2: SimpleClient.java

image from book
 1     import java.net.*; 2     import java.io.*; 3     import javax.swing.*; 4     import java.awt.event.*; 5     import java.awt.*; 6 7     public class SimpleClient extends JFrame implements ActionListener { 8 9         private JPanel panel1 = null; 10        private JPanel panel2 = null; 11        private JTextArea textarea1 = null; 12        private JTextArea textarea2 = null; 13        private JButton button1 = null; 14        private JScrollPane scrollpane1 = null; 15        private JScrollPane scrollpane2 = null; 16 17        Socket socket = null; 18        DataInputStream dis = null; 19        DataOutputStream dos = null; 20 21        public SimpleClient(String url){ 22         super("SimpleClient"); 23         panel1 = new JPanel(); 24         panel2 = new JPanel(); 25         textarea1 = new JTextArea(12, 25); 26         textarea1.append("Enter your message here."); 27         scrollpane1 = new JScrollPane(textarea1); 28         textarea2 = new JTextArea(12, 25); 29         textarea2.setEnabled(false); 30         textarea2.append("Server response messages will appear here.\n"); 31         scrollpane2 = new JScrollPane(textarea2); 32         panel1.add(scrollpane1); 33         panel1.add(scrollpane2); 34         button1 = new JButton("Send"); 35         button1.addActionListener(this); 36         panel2.add(button1); 37         this.getContentPane().add(panel1); 38         this.getContentPane().add(BorderLayout.SOUTH, panel2); 39         this.setSize(620, 300); 40         this.setLocation(200, 200); 41         this.show(); 42 43         try{ 44           System.out.println("Creating Socket object..."); 45           socket = new Socket(url, 5001); 46           System.out.println("Creating IOStream objects..."); 47 48           dos = new DataOutputStream(socket.getOutputStream()); 49           dis = new DataInputStream(socket.getInputStream()); 50 51          }catch(Exception e){ 52            e.printStackTrace(); 53          } 54           System.out.println("Ready to send commands to the server..."); 55        } 56 57        public void actionPerformed(ActionEvent ae){ 58          if(ae.getActionCommand().equals("Send")){ 59            System.out.println(textarea1.getText()); 60 61           System.out.println("Sending text to server..."); 62           try{ 63           dos.writeUTF(textarea1.getText()); 64           }catch(Exception e){ 65             e.printStackTrace(); 66            } 67 68           System.out.println("Reading text from server..."); 69            try{ 70            textarea2.append(dis.readUTF() + "\n"); 71            }catch(Exception e){ 72               e.printStackTrace(); 73               } 74 75            if(textarea1.getText().equals("disconnect client") || 76                              textarea1.getText().equals("shutdown server")){ 77              System.out.println("Client shutting down..."); 78              try{ 79                 socket.close(); 80               }catch(Exception e){ 81                 e.printStackTrace(); 82                 } 83              System.exit(0); 84             } 85          } 86        } // end actionPerformed() method 87 88 89        public static void main(String[] args){ 90          System.out.println("SimpleClient lives!"); 91          try{ 92          new SimpleClient(args[0]); 93          }catch(NullPointerException npe){ 94            System.out.println("Usage: java SimpleClient <host>"); 95          } 96           catch(ArrayIndexOutOfBoundsException oobe){ 97            System.out.println("Usage: java SimpleClient <host>"); 98           } 99           catch(Exception e){ 100           e.printStackTrace(); 101          } 102       }// end main() 103     }
image from book

Referring to example 20.2 — on lines 9 through 19 the SimpleClient class declares several Swing component fields, a Socket field, a DataInputStream field, and a DataOutputStream field. The constructor method initializes the Swing components as expected. Starting on line 43 the Socket and IOStream objects are created inside the body of a try-catch block. Notice that the Socket object is created first. If this is successful then the IOStream objects are created by calling the Socket.getInputStream() and getOutputStream() methods as was done in the server code.

Once the application is up and running all interaction between the client and server application takes place in the body of the actionPerformed() method that starts on line 57. Messages entered in textarea1 are sent to the server. Messages received from the server are written to textarea2. The message “disconnect client” causes the client to shutdown and the server to return to listening for incoming client connections. The message “shutdown server” shuts down both the client and server applications.

The main() method begins on line 89. The SimpleClient application must be started with a host URL or IP address. If the user fails to enter a URL or IP address the application will print a short message showing how to properly use the application.

Now, let’s run these applications and see how they work.

Running The Examples

Start by running the SimpleServer application first. It must be running before it can service incoming client connections. Figure 20-5 shows how the SimpleServer console looks after it has started and is waiting for incoming client connections. You can now start up an instance of SimpleClient and connect to the server.

image from book
Figure 20-5: SimpleServer Running & Waiting for Incoming Client Connections

Figure 20-6 shows the console and GUI for the SimpleClient application. The SimpleClient application is started with the IP address of 127.0.0.1 (the localhost address) since this is where the SimpleServer application is running. If it were running on another machine on your network you would need to use the IP address for that machine.

image from book
Figure 20-6: SimpleClient Console Output and GUI

When the SimpleClient application starts up it prints out a few diagnostic messages as it creates the Socket object and IOStream objects. It then prints a message to the console indicating its readiness to send messages to the server.

When the client connection is made the SimpleServer application prints diagnostic messages to the console indicating that it is creating the IOStream objects and processing client string input. Figure 20-7 shows the SimpleServer console at this stage of the game.

image from book
Figure 20-7: SimpleServer Console After Detecting Incoming Client Connection

To send a message to the server using the SimpleClient application enter a message in the left text area. Note that all the text contained in the left text area will be sent to the server and echoed back from the server into the right text area. Figure 20-8 shows an exchange of several messages between the client and server applications.

image from book
Figure 20-8: Several Messages Exchanged with the Server From SimpleClient

To shut down the client enter “disconnect client” in the left text area and click the send button. To shut down both the client and the server applications send the “shutdown server” message.

A Few Words About Protocol

The SimpleServer application establishes a set of ground rules for clients to follow when attempting to communicate. In the case of SimpleServer the protocol is simple: Send it a UTF message and it will send it right back to you, tit-for-tat. The strings “disconnect client” and “shutdown server” can be considered special commands the server recognizes to execute two special functions. (returning to listen for incoming client connections and shutting down) Clients that attempt to communicate with SimpleServer must be clued in to its protocol requirements, otherwise, not much will happen when the connection is made, if it’s made at all!

Another aspect of the SimpleServer protocol is that it is connection oriented. This means that the client and server applications maintain a socket connection open for the duration of their communication session.

Quick Review

Simple socket-based server applications utilize a ServerSocket object to listen for incoming client connections. A ServerSocket object listens for incoming connections on a specified port. Client applications utilize a Socket object to connect to a server application on a specified URL or IP address and port number. The localhost IP address can be used during testing.

The Socket object is used to create the IOStream objects using the getInputStream() and getOutputStream() methods. Once the IOStream objects are created on both the client and server ends the applications can communicate with each other via an established set of rules. (a.k.a., a custom application protocol). A client-server protocol is connection oriented if a socket connection is maintained open between the client and server applications for the duration of their communication session.




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