Client/Server Interaction with Stream Socket Connections

Client Server Interaction with Stream Socket Connections

Figure 24.5 and Fig. 24.7 use stream sockets to demonstrate a simple client/server chat application. The server waits for a client connection attempt. When a client connects to the server, the server application sends the client a String object (recall that Strings are Serializable objects) indicating that the connection was successful. Then the client displays the message. The client and server applications each provide textfields that allow the user to type a message and send it to the other application. When the client or the server sends the string "TERMINATE", the connection terminates. Then the server waits for the next client to connect. The declaration of class Server appears in Fig. 24.5. The declaration of class Client appears in Fig. 24.7. The screen captures showing the execution between the client and the server are shown as part of Fig. 24.7.

Figure 24.5. Server portion of a client/server stream-socket connection.

(This item is displayed on pages 1120 - 1123 in the print version)

 1 // Fig. 24.5: Server.java
 2 // Set up a Server that will receive a connection from a client, send
 3 // a string to the client, and close the connection.
 4 import java.io.EOFException;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutputStream;
 8 import java.net.ServerSocket;
 9 import java.net.Socket; 
10 import java.awt.BorderLayout;
11 import java.awt.event.ActionEvent;
12 import java.awt.event.ActionListener;
13 import javax.swing.JFrame;
14 import javax.swing.JScrollPane;
15 import javax.swing.JTextArea;
16 import javax.swing.JTextField;
17 import javax.swing.SwingUtilities;
18 
19 public class Server extends JFrame
20 {
21 private JTextField enterField; // inputs message from user
22 private JTextArea displayArea; // display information to user
23 private ObjectOutputStream output; // output stream to client
24 private ObjectInputStream input; // input stream from client
25 private ServerSocket server; // server socket 
26 private Socket connection; // connection to client
27 private int counter = 1; // counter of number of connections
28 
29 // set up GUI
30 public Server()
31 {
32 super ( "Server" );
33 
34 enterField = new JTextField(); // create enterField
35 enterField.setEditable( false );
36 enterField.addActionListener(
37 new ActionListener()
38 {
39 // send message to client
40 public void actionPerformed( ActionEvent event )
41 {
42 sendData( event.getActionCommand() );
43 enterField.setText( "" );
44 } // end method actionPerformed
45 } // end anonymous inner class
46 ); // end call to addActionListener
47 
48 add( enterField, BorderLayout.NORTH );
49 
50 displayArea = new JTextArea(); // create displayArea
51 add( new JScrollPane( displayArea ), BorderLayout.CENTER );
52 
53 setSize( 300, 150 ); // set size of window
54 setVisible( true ); // show window
55 } // end Server constructor
56 
57 // set up and run server
58 public void runServer()
59 {
60 try // set up server to receive connections; process connections
61 {
62 server = new ServerSocket( 12345, 100 ); // create ServerSocket
63 
64 while ( true )
65 {
66 try
67 {
68 waitForConnection(); // wait for a connection
69 getStreams(); // get input & output streams
70 processConnection(); // process connection
71 } // end try
72 catch ( EOFException eofException )
73 {
74 displayMessage( "
Server terminated connection" );
75 } // end catch
76 finally
77 {
78 closeConnection(); // close connection
79 counter++;
80 } // end finally
81 } // end while
82 } // end try
83 catch ( IOException ioException )
84 {
85 ioException.printStackTrace();
86 } // end catch
87 } // end method runServer
88 
89 // wait for connection to arrive, then display connection info
90 private void waitForConnection() throws IOException
91 {
92 displayMessage( "Waiting for connection
" );
93 connection = server.accept(); // allow server to accept connection
94 displayMessage( "Connection " + counter + " received from: " +
95 connection.getInetAddress().getHostName() );
96 } // end method waitForConnection
97 
98 // get streams to send and receive data
99 private void getStreams() throws IOException
100 {
101 // set up output stream for objects
102 output = new ObjectOutputStream( connection.getOutputStream() ); 
103 output.flush(); // flush output buffer to send header information
104 
105 // set up input stream for objects
106 input = new ObjectInputStream( connection.getInputStream() );
107 
108 displayMessage( "
Got I/O streams
" );
109 } // end method getStreams
110 
111 // process connection with client
112 private void processConnection() throws IOException
113 {
114 String message = "Connection successful";
115 sendData( message ); // send connection successful message
116 
117 // enable enterField so server user can send messages
118 setTextFieldEditable( true );
119 
120 do // process messages sent from client
121 {
122 try // read message and display it
123 {
124 message = ( String ) input.readObject(); // read new message
125 displayMessage( "
" + message ); // display message
126 } // end try
127 catch ( ClassNotFoundException classNotFoundException )
128 {
129 displayMessage( "
Unknown object type received" );
130 } // end catch
131 
132 } while ( !message.equals( "CLIENT>>> TERMINATE" ) );
133 } // end method processConnection
134 
135 // close streams and socket
136 private void closeConnection()
137 {
138 displayMessage( "
Terminating connection
" );
139 setTextFieldEditable( false ); // disable enterField
140 
141 try
142 {
143 output.close(); // close output stream
144 input.close(); // close input stream 
145 connection.close(); // close socket 
146 } // end try
147 catch ( IOException ioException )
148 {
149 ioException.printStackTrace();
150 } // end catch
151 } // end method closeConnection
152 
153 // send message to client
154 private void sendData( String message )
155 {
156 try // send object to client
157 {
158 output.writeObject( "SERVER>>> " + message );
159 output.flush(); // flush output to client 
160 displayMessage( "
SERVER>>> " + message );
161 } // end try
162 catch ( IOException ioException )
163 {
164 displayArea.append( "
Error writing object" );
165 } // end catch
166 } // end method sendData
167 
168 // manipulates displayArea in the event-dispatch thread
169 private void displayMessage( final String messageToDisplay )
170 {
171 SwingUtilities.invokeLater(
172 new Runnable()
173 {
174 public void run() // updates displayArea
175 {
176 displayArea.append( messageToDisplay ); // append message
177 } // end method run
178 } // end anonymous inner class
179 ); // end call to SwingUtilities.invokeLater
180 } // end method displayMessage
181 
182 // manipulates enterField in the event-dispatch thread
183 private void setTextFieldEditable( final boolean editable )
184 {
185 SwingUtilities.invokeLater(
186 new Runnable()
187 {
188 public void run() // sets enterField's editability
189 {
190 enterField.setEditable( editable );
191 } // end method run
192 } // end inner class
193 ); // end call to SwingUtilities.invokeLater
194 } // end method setTextFieldEditable
195 } // end class Server

Server Class

Server's constructor (lines 3055) creates the server's GUI, which contains a JTextField and a JTextArea. Server displays its output in the JTextArea. When the main method (lines 712 of Fig. 24.6) executes, it creates a Server object, specifies the window's default close operation and calls method runServer (declared at lines 5887).

Figure 24.6. Test class for Server.

(This item is displayed on page 1124 in the print version)

 1 // Fig. 24.6: ServerTest.java
 2 // Test the Server application.
 3 import javax.swing.JFrame;
 4
 5 public class ServerTest
 6 {
 7 public static void main( String args[] )
 8 {
 9 Server application = new Server(); // create server
10 application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
11 application.runServer(); // run server application
12 } // end main
13 } // end class ServerTest

Method runServer sets up the server to receive a connection and processes one connection at a time. Line 62 creates a ServerSocket called server to wait for connections. The ServerSocket listens for a connection from a client at port 12345. The second argument to the constructor is the number of connections that can wait in a queue to connect to the server (100 in this example). If the queue is full when a client attempts to connect, the server refuses the connection.

Common Programming Error 24.1

Specifying a port that is already in use or specifying an invalid port number when creating a ServerSocket results in a BindException.

Line 68 calls method waitForConnection (declared at lines 9096) to wait for a client connection. After the connection is established, line 69 calls method getStreams (declared at lines 99109) to obtain references to the streams for the connection. Line 70 calls method processConnection (declared at lines 112133) to send the initial connection message to the client and to process all messages received from the client. The finally block (lines 7680) terminates the client connection by calling method closeConnection (lines 136151) even if an exception occurs. Method displayMessage (lines 169180) is called from these methods to use the event-dispatch thread to display messages in the application's JTextArea.

In method waitForConnection (lines 9096), line 93 uses ServerSocket method accept to wait for a connection from a client. When a connection occurs, the resulting Socket is assigned to connection. Method accept blocks until a connection is received (i.e., the thread in which accept is called stops executing until a client connects). Lines 9495 output the host name of the computer that made the connection. Socket method getInetAddress returns an InetAddress (package java.net) containing information about the client computer. InetAddress method getHostName returns the host name of the client computer. For example, there is a special IP address (127.0.0.1) and host name (localhost) that is useful for testing networking applications on your local computer (this is also known as the loopback address). If getHostName is called on an InetAddress containing 127.0.0.1, the corresponding host name returned by the method would be localhost.

Method getStreams (lines 99109) obtains references to the Socket's streams and uses them to initialize an ObjectOutputStream (line 102) and an ObjectInputStream (line 106), respectively. Note the call to ObjectOutputStream method flush at line 103. This statement causes the ObjectOutputStream on the server to send a stream header to the corresponding client's ObjectInputStream. The stream header contains such information as the version of object serialization being used to send objects. This information is required by the ObjectInputStream so that it can prepare to receive those objects correctly.

Software Engineering Observation 24.5

When using an ObjectOutputStream and ObjectInputStream to send and receive data over a network connection, always create the ObjectOutputStream first and flush the stream so that the client's ObjectInputStream can prepare to receive the data. This is required only for networking applications that communicate using ObjectOutputStream and ObjectInputStream.

Performance Tip 24.3

A computer's input and output components are typically much slower than its memory. Output buffers typically are used to increase the efficiency of an application by sending larger amounts of data fewer times, thus reducing the number of times an application accesses the computer's input and output components.

Line 114 of method processConnection (lines 112133) calls method sendData to send "SERVER>>> Connection successful" as a string to the client. The loop at lines 120132 executes until the server receives the message "CLIENT>>> TERMINATE". Line 124 uses ObjectInputStream method readObject to read a String from the client. Line 125 invokes method displayMessage to append the message to the JTextArea.

When the transmission is complete, method processConnection returns, and the program calls method closeConnection (lines 136151) to close the streams associated with the Socket and close the Socket. Then the server waits for the next connection attempt from a client by continuing with line 68 at the beginning of the while loop.

When the user of the server application enters a string in the textfield and presses the Enter key, the program calls method actionPerformed (lines 4044), which reads the string from the textfield and calls utility method sendData (lines 154166) to send the string to the client. Method sendData writes the object, flushes the output buffer and appends the same string to the textarea in the server window. It is not necessary to invoke displayMessage to modify the textarea here, because method sendData is called from an event handlerthus, sendData executes as part of the event-dispatch thread.

Note that Server receives a connection, processes it, closes it and waits for the next connection. A more likely scenario would be a Server that receives a connection, sets it up to be processed as a separate thread of execution, then immediately waits for new connections. The separate threads that process existing connections can continue to execute while the Server concentrates on new connection requests. This makes the server more efficient, because multiple client requests can be processed concurrently. We demonstrate a multithreaded server in Section 24.8.

Client Class

Like class Server, class Client's (Fig. 24.7) constructor (lines 2956) creates the GUI of the application (a JTextField and a JTextArea). Client displays its output in the textarea. When method main (lines 719 of Fig. 24.8) executes, it creates an instance of class Client, specifies the window's default close operation and calls method runClient (declared at lines 5979). In this example, you can execute the client from any computer on the Internet and specify the IP address or host name of the server computer as a commandline argument to the program. For example, the command

 java Client 192.168.1.15

attempts to connect to the Server on the computer with IP address 192.168.1.15.

Figure 24.7. Client portion of a stream-socket connection between client and server.

(This item is displayed on pages 1126 - 1129 in the print version)

 1 // Fig. 24.7: Client.java
 2 // Client that reads and displays information sent from a Server.
 3 import java.io.EOFException;
 4 import java.io.IOException;
 5 import java.io.ObjectInputStream;
 6 import java.io.ObjectOutputStream;
 7 import java.net.InetAddress;
 8 import java.net.Socket; 
 9 import java.awt.BorderLayout;
10 import java.awt.event.ActionEvent;
11 import java.awt.event.ActionListener;
12 import javax.swing.JFrame;
13 import javax.swing.JScrollPane;
14 import javax.swing.JTextArea;
15 import javax.swing.JTextField;
16 import javax.swing.SwingUtilities;
17 
18 public class Client extends JFrame
19 {
20 private JTextField enterField; // enters information from user
21 private JTextArea displayArea; // display information to user
22 private ObjectOutputStream output; // output stream to server
23 private ObjectInputStream input; // input stream from server
24 private String message = ""; // message from server
25 private String chatServer; // host server for this application
26 private Socket client; // socket to communicate with server
27 
28 // initialize chatServer and set up GUI
29 public Client( String host )
30 {
31 super( "Client" );
32 
33 chatServer = host; // set server to which this client connects
34 
35 enterField = new JTextField(); // create enterField
36 enterField.setEditable( false );
37 enterField.addActionListener(
38 new ActionListener()
39 {
40 // send message to server
41 public void actionPerformed( ActionEvent event )
42 {
43 sendData( event.getActionCommand() );
44 enterField.setText( "" );
45 } // end method actionPerformed
46 } // end anonymous inner class
47 ); // end call to addActionListener
48 
49 add( enterField, BorderLayout.NORTH );
50 
51 displayArea = new JTextArea(); // create displayArea
52 add( new JScrollPane( displayArea ), BorderLayout.CENTER );
53 
54 setSize( 300, 150 ); // set size of window
55 setVisible( true ); // show window
56 } // end Client constructor
57 
58 // connect to server and process messages from server
59 public void runClient()
60 {
61 try // connect to server, get streams, process connection
62 {
63 connectToServer(); // create a Socket to make connection
64 getStreams(); // get the input and output streams
65 processConnection(); // process connection
66 } // end try
67 catch ( EOFException eofException )
68 {
69 displayMessage( "
Client terminated connection" );
70 } // end catch
71 catch ( IOException ioException )
72 {
73 ioException.printStackTrace();
74 } // end catch
75 finally
76 {
77 closeConnection(); // close connection
78 } // end finally
79 } // end method runClient
80 
81 // connect to server
82 private void connectToServer() throws IOException
83 {
84 displayMessage( "Attempting connection
" );
85 
86 // create Socket to make connection to server
87 client = new Socket( InetAddress.getByName( chatServer ), 12345 );
88 
89 // display connection information
90 displayMessage( "Connected to: " +
91 client.getInetAddress().getHostName() );
92 } // end method connectToServer
93 
94 // get streams to send and receive data
95 private void getStreams() throws IOException
96 {
97 // set up output stream for objects
98 output = new ObjectOutputStream( client.getOutputStream() ); 
99 output.flush(); // flush output buffer to send header information
100 
101 // set up input stream for objects
102 input = new ObjectInputStream( client.getInputStream() );
103 
104 displayMessage( "
Got I/O streams
" );
105 } // end method getStreams
106 
107 // process connection with server
108 private void processConnection() throws IOException
109 {
110 // enable enterField so client user can send messages
111 setTextFieldEditable( true );
112 
113 do // process messages sent from server
114 {
115 try // read message and display it
116 {
117 message = ( String ) input.readObject(); // read new message
118 displayMessage( "
" + message ); // display message
119 } // end try
120 catch ( ClassNotFoundException classNotFoundException )
121 {
122 displayMessage( "
Unknown object type received" );
123 } // end catch
124 
125 } while ( !message.equals( "SERVER>>> TERMINATE" ) );
126 } // end method processConnection
127 
128 // close streams and socket
129 private void closeConnection()
130 {
131 displayMessage( "
Closing connection" );
132 setTextFieldEditable( false ); // disable enterField
133 
134 try
135 {
136 output.close(); // close output stream
137 input.close(); // close input stream 1
138 client.close(); // close socket 
139 } // end try
140 catch ( IOException ioException )
141 {
142 ioException.printStackTrace();
143 } // end catch
144 } // end method closeConnection
145 
146 // send message to server
147 private void sendData( String message )
148 {
149 try // send object to server
150 {
151 output.writeObject( "CLIENT>>> " + message );
152 output.flush(); // flush data to output 
153 displayMessage( "
CLIENT>>> " + message );
154 } // end try
155 catch ( IOException ioException )
156 {
157 displayArea.append( "
Error writing object" );
158 } // end catch
159 } // end method sendData
160 
161 // manipulates displayArea in the event-dispatch thread
162 private void displayMessage( final String messageToDisplay )
163 {
164 SwingUtilities.invokeLater(
165 new Runnable()
166 {
167 public void run() // updates displayArea
168 {
169 displayArea.append( messageToDisplay );
170 } // end method run
171 } // end anonymous inner class
172 ); // end call to SwingUtilities.invokeLater
173 } // end method displayMessage
174 
175 // manipulates enterField in the event-dispatch thread
176 private void setTextFieldEditable( final boolean editable )
177 {
178 SwingUtilities.invokeLater(
179 new Runnable()
180 {
181 public void run() // sets enterField's editability
182 {
183 enterField.setEditable( editable );
184 } // end method run
185 } // end anonymous inner class
186 ); // end call to SwingUtilities.invokeLater
187 } // end method setTextFieldEditable
188 } // end class Client

Figure 24.8. Class that tests the Client.

(This item is displayed on pages 1130 - 1131 in the print version)

 1 // Fig. 24.8: ClientTest.java
 2 // Test the Client class.
 3 import javax.swing.JFrame;
 4
 5 public class ClientTest
 6 {
 7 public static void main( String args[] )
 8 {
 9 Client application; // declare client application
10
11 // if no command line args
12 if ( args.length == 0 )
13 application = new Client( "127.0.0.1" ); // connect to localhost
14 else
15 application = new Client( args[ 0 ] ); // use args to connect 
16
17 application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
18 application.runClient(); // run client application
19 } // end main
20 } // end class ClientTest
 

Client method runClient (lines 5979) sets up the connection to the server, processes messages received from the server and closes the connection when communication is complete. Line 63 calls method connectToServer (declared at lines 8292) to perform the connection. After connecting, line 64 calls method getStreams (declared at lines 95105) to obtain references to the Socket's stream objects. Then line 65 calls method processConnection (declared at lines 108126) to receive and display messages sent from the server. The finally block (lines 7578) calls closeConnection (lines 129144) to close the streams and the Socket even if an exception has occurred. Method displayMessage (lines 162173) is called from these methods to use the event-dispatch thread to display messages in the application's textarea.

Method connectToServer (lines 8292) creates a Socket called client (line 87) to establish a connection. The method passes two arguments to the Socket constructorthe IP address of the server computer and the port number (12345) where the server application is awaiting client connections. In the first argument, InetAddress static method getByName returns an InetAddress object containing the IP address specified as a command-line argument to the application (or 127.0.0.1 if no command-line arguments are specified). Method getByName can receive a string containing either the actual IP address or the host name of the server. The first argument also could have been written other ways. For the localhost address 127.0.0.1, the first argument could be

 InetAddress.getByName( "localhost" )

or

 InetAddress. getLocalHost()

Also, there are versions of the Socket constructor that receive a string for the IP address or host name. The first argument could have been specified as "127.0.0.1" or "localhost". We chose to demonstrate the client/server relationship by connecting between applications executing on the same computer (localhost). Normally, this first argument would be the IP address of another computer. The InetAddress object for another computer can be obtained by specifying the computer's IP address or host name as the argument to InetAddress method getByName. The Socket constructor's second argument is the server port number. This must match the port number at which the server is waiting for connections (called the handshake point). Once the connection is made, lines 9091 display a message in the text area indicating the name of the server computer to which the client has connected.

The Client uses an ObjectOutputStream to send data to the server and an ObjectInputStream to receive data from the server. Method getStreams (lines 95105) creates the ObjectOutputStream and ObjectInputStream objects that use the streams associated with the client socket.

Method processConnection (lines 108126) contains a loop that executes until the client receives the message "SERVER>>> TERMINATE". Line 117 reads a String object from the server. Line 118 invokes displayMessage to append the message to the textarea.

When the transmission is complete, method closeConnection (lines 129144) closes the streams and the Socket.

When the user of the client application enters a string in the textfield and presses the Enter key, the program calls method actionPerformed (lines 4145) to read the string and invoke utility method sendData (147159) to send the string to the server. Method sendData writes the object, flushes the output buffer and appends the same string to the JTextArea in the client window. Once again, it is not necessary to invoke utility method displayMessage to modify the textarea here, because method sendData is called from an event handler.

Introduction to Computers, the Internet and the World Wide Web

Introduction to Java Applications

Introduction to Classes and Objects

Control Statements: Part I

Control Statements: Part 2

Methods: A Deeper Look

Arrays

Classes and Objects: A Deeper Look

Object-Oriented Programming: Inheritance

Object-Oriented Programming: Polymorphism

GUI Components: Part 1

Graphics and Java 2D™

Exception Handling

Files and Streams

Recursion

Searching and Sorting

Data Structures

Generics

Collections

Introduction to Java Applets

Multimedia: Applets and Applications

GUI Components: Part 2

Multithreading

Networking

Accessing Databases with JDBC

Servlets

JavaServer Pages (JSP)

Formatted Output

Strings, Characters and Regular Expressions

Appendix A. Operator Precedence Chart

Appendix B. ASCII Character Set

Appendix C. Keywords and Reserved Words

Appendix D. Primitive Types

Appendix E. (On CD) Number Systems

Appendix F. (On CD) Unicode®

Appendix G. Using the Java API Documentation

Appendix H. (On CD) Creating Documentation with javadoc

Appendix I. (On CD) Bit Manipulation

Appendix J. (On CD) ATM Case Study Code

Appendix K. (On CD) Labeled break and continue Statements

Appendix L. (On CD) UML 2: Additional Diagram Types

Appendix M. (On CD) Design Patterns

Appendix N. Using the Debugger

Inside Back Cover



Java(c) How to Program
Java How to Program (6th Edition) (How to Program (Deitel))
ISBN: 0131483986
EAN: 2147483647
Year: 2003
Pages: 615

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