AhmadLang / Java, How To Program, 2004
.pdf
134
135Box box = new Box( BoxLayout.X_AXIS ); // create new box for layout
136box.add( new JScrollPane( inputArea ) ); // add input area to box
137box.add( sendButton ); // add send button to box
138messagePanel.add( box, BorderLayout.SOUTH ); // add box to panel
140// create JLabel for statusBar with a recessed border
141statusBar = new JLabel( "Not Connected" );
142statusBar.setBorder( new BevelBorder( BevelBorder.LOWERED ) );
144add( buttonPanel, BorderLayout.NORTH ); // add button panel
145add( messagePanel, BorderLayout.CENTER ); // add message panel
146add( statusBar, BorderLayout.SOUTH ); // add status bar
148// add WindowListener to disconnect when user quits
149addWindowListener (
150new WindowAdapter ()
151{
152// disconnect from server and exit application
153public void windowClosing ( WindowEvent event )
154{
155 |
messageManager |
.disconnect( messageListener ); |
156 |
System.exit( 0 |
); |
157} // end method windowClosing
158} // end anonymous inner class
159); // end call to addWindowListener
160} // end ClientGUI constructor
161
162// ConnectListener listens for user requests to connect to server
163private class ConnectListener implements ActionListener
164{
165// connect to server and enable/disable GUI components
166public void actionPerformed( ActionEvent event )
167{
168// connect to server and route messages to messageListener
169messageManager.connect( messageListener );
170
171// prompt for userName
172userName = JOptionPane.showInputDialog(
173ClientGUI.this, "Enter user name:" );
175messageArea.setText( "" ); // clear messageArea
176connectButton.setEnabled( false ); // disable connect
177connectMenuItem.setEnabled( false ); // disable connect
178disconnectButton.setEnabled( true ); // enable disconnect
179disconnectMenuItem.setEnabled( true ); // enable disconnect
180sendButton.setEnabled( true ); // enable send button
181inputArea.setEditable( true ); // enable editing for input area
182inputArea.requestFocus(); // set focus to input area
183statusBar.setText( "Connected: " + userName ); // set text
184} // end method actionPerformed
185} // end ConnectListener inner class
187// DisconnectListener listens for user requests to disconnect
188// from DeitelMessengerServer
189private class DisconnectListener implements ActionListener
190{
191// disconnect from server and enable/disable GUI components
192public void actionPerformed( ActionEvent event )
193{
194// disconnect from server and stop routing messages
195messageManager.disconnect( messageListener );
196sendButton.setEnabled( false ); // disable send button
197disconnectButton.setEnabled( false ); // disable disconnect
198disconnectMenuItem.setEnabled( false ); // disable disconnect
199inputArea.setEditable( false ); // disable editing
200connectButton.setEnabled( true ); // enable connect
201connectMenuItem.setEnabled( true ); // enable connect
202statusBar.setText( "Not Connected" ); // set status bar text
203} // end method actionPerformed
204} // end DisconnectListener inner class
205
206// MyMessageListener listens for new messages from MessageManager and
207// displays messages in messageArea using MessageDisplayer.
208private class MyMessageListener implements MessageListener
209{
wrapping, and line 114 disables editing the inputArea. When the client connects to the chat server,
ConnectListener enables the inputArea to allow the user to type new messages.
Lines 117118 create an ImageIcon for sendButton. Line 120 creates sendButton, which the user can click to send a message. Line 121 disables sendButtonthe ConnectListener enables the sendButton when the client connects to the chat server. Lines 122133 add an ActionListener to sendButton. Lines 128129 invoke method sendMessage of interface MessageManager with the userName and inputArea text as arguments. This statement sends the user's name and message as a new chat message to DeitelMessengerServer. Line 130 clears the inputArea for the next message.
Lines 135138 use a horizontal Box container to arrange components inputArea and sendButton. Line 136 places inputArea in a JScrollPane to enable scrolling of long messages. Line 138 adds the Box containing inputArea and sendButton to the SOUTH region of messagePanel. Line 141 creates the statusBar JLabel. This label displays whether the client is connected to or disconnected from the chat server. Line 142 invokes method setBorder of class JLabel and creates a new BevelBorder of type BevelBorder.LOWERED. This border makes the label appear recessed, as is common with status bars in many applications. Lines
144146 add buttonPanel, messagePanel and statusBar to the ClientGUI.
Lines 149159 add a WindowListener to the ClientGUI. Line 155 invokes method disconnect of interface MessageManager to disconnect from the chat server in case the user quits while still connected. Then line 156 terminates the application.
Inner class ConnectListener (lines 163185) handles events from connectButton and connectMenuItem. Line 169 invokes MessageManager method connect to connect to the chat server. Line 169 passes as an argument to method connect the MessageListener to which new messages should be delivered. Lines 172173 prompt the user for a user name, and line 175 clears the messageArea. Lines 176181 enable the components for disconnecting from the server and for sending messages and disable the components for connecting to the server. Line 182 invokes inputArea's requestFocus method (inherited from class Component) to place the text-input cursor in the inputArea so that the user can immediately begin typing a message.
Inner class DisconnectListener (lines 189204) handles events from disconnectButton and disconnectMenuItem. Line 195 invokes MessageManager method disconnect to disconnect from the chat server. Lines 196201 disable the components for sending messages and the components for disconnecting, then enable the components for connecting to the chat server.
Inner class MyMessageListener (lines 208217) implements interface MessageListener to receive incoming messages from the MessageManager. When a new message is received, the MessageManager invokes method messageReceived (lines 211216) with the user name of the sender and the message body. Lines 214215 invoke SwingUtilities method invokeLater with a MessageDisplayer object that appends the new message to messageArea. Recall, from Chapter 23, that Swing components should be accessed only from the event-dispatch thread. Method messageReceived is invoked by the PacketReceiver in class SocketMessageManager and therefore cannot append the message text to messageArea directly, as this would occur in PacketReceiver, not the event-dispatch thread.
[Page 1179]
Inner class MessageDisplayer (lines 221239) implements interface Runnable to provide a thread-safe way to append text to the messageArea. The MessageDisplayer constructor (lines 227231) takes as arguments the user name and the message to send. Method run (lines 234238) appends the user name, "> " and
messageBody to messageArea.
Class DeitelMessenger (Fig. 24.29) launches the client for the DeitelMessengerServer. Lines 1520 create a new SocketMessageManager to connect to the DeitelMessengerServer with the IP address specified as a command-line argument to the application (or localhost, if no address is provided). Lines 2326 create a
ClientGUI for the MessageManager, set the ClientGUI size and make the ClientGUI visible.
Figure 24.29. DeitelMessenger application for participating in a
DeitelMessengerServer chat session.
(This item is displayed on pages 1179 - 1180 in the print version)
1 |
// Fig. |
24.29: DeitelMessenger.java |
|
2 |
// |
DeitelMessenger is a chat application that uses a ClientGUI |
|
3 |
// |
and SocketMessageManager to communicate with DeitelMessengerServer. |
|
4 |
package |
com.deitel.messenger.sockets.client; |
|
5 |
|
|
|
6import com.deitel.messenger.MessageManager;
7import com.deitel.messenger.ClientGUI;
8
9public class DeitelMessenger
10{
11public static void main( String args[] )
12{
13MessageManager messageManager; // declare MessageManager
15if ( args.length == 0 )
16// connect to localhost
17messageManager = new SocketMessageManager( "localhost" );
18else
19// connect using command-line arg
20messageManager = new SocketMessageManager( args[ 0 ] );
22// create GUI for SocketMessageManager
23ClientGUI clientGUI = new ClientGUI( messageManager );
24clientGUI.setSize( 300, 400 ); // set window size
25clientGUI.setResizable( false ); // disable resizing
26clientGUI.setVisible( true ); // show window
27} // end main
28} // end class DeitelMessenger
[View full size image]
[Page 1181]
java com.deitel.messenger.sockets.client.DeitelMessenger
to execute the client and connect to the DeitelMessengerServer running on your local computer. If the server resides on another computer, follow the preceding command with the hostname or IP address of that computer. The preceding command is equivalent to
java com.deitel.messenger.sockets.client.DeitelMessenger localhost
or
java com.deitel.messenger.sockets.client.DeitelMessenger 127.0.0.1
Deitel Messenger Case Study Summary
The Deitel messenger case study is a significant application that uses many intermediate Java features, such as networking with Sockets, DatagramPackets and MulticastSockets, multithreading and Swing GUI. The case study also demonstrates good software engineering practices by separating interface from implementation, enabling developers to build MessageManagers for different network protocols and MessageListeners that provide different user interfaces. You should now be able to apply these techniques to your own, more complex, Java projects.
[Page 1181 (continued)]
24.11. Wrap-Up
In this chapter, you have learned the basics of network programming in Java. You learned two different methods of sending data over a network: streams-based networking using TCP/IP and datagrams-based networking using UDP. You also learned about multicasting, which allows you to send data to multiple clients with a single command. In the next chapter, you will learn basic database concepts, how to interact with data in a database using SQL and how to use JDBC to allow Java applications to manipulate database data.
[Page 1181 (continued)]
Summary
Java provides stream sockets and datagram sockets. With stream sockets, a process establishes
a connection to another process. While the connection is in place, data flows between the processes in streams. Stream sockets are said to provide a connection-oriented service. The protocol used for transmission is the popular TCP (Transmission Control Protocol).
With datagram sockets, individual packets of information are transmitted. UDP (User Datagram
Protocol) is a connectionless service that does not guarantee that packets will not can be lost, duplicated or arrive out of sequence. Extra programming is required on the programmer's part to deal with these problems.
The HTTP protocol (Hypertext Transfer Protocol) that forms the basis of the Web uses URIs
(Uniform Resource Identifiers) to locate data on the Internet. Common URIs represent files or directories and can represent complex tasks such as database lookups and Internet searches. A URI that represents a document is called a URL (Uniform Resource Locator).
Web browsers often restrict an applet so that it can communicate only with the machine from which it was originally downloaded.
Applet method getAppletContext returns a reference to an AppletContext object that
represents the applet's environment (i.e., the browser in which the applet is executing). AppletContext method showDocument receives a URL as an argument and passes it to the AppletContext (i.e., the browser), which displays the Web resource associated with that URL. A second version of showDocument enables an applet to specify the target frame in which to display a Web resource. Special target frames include _blank (display in a new Web browser window), _self (display in the same frame as the applet) and _top (remove the current frames, then display in the current window).
[Page 1182]
JEditorPane method setPage downloads the document specified by its argument and displays it in the JEditorPane.
Typically, an HTML document contains hyperlinkstext, images or GUI components that, when
clicked, link to another document on the Web. If an HTML document is displayed in a JEditorPane and the user clicks a hyperlink, the JEditorPane generates a HyperlinkEvent and notifies all registered HyperlinkListeners of the event.
HyperlinkEvent method getEventType determines the event type. HyperlinkEvent contains
nested class EventType, which declares three hyperlink event types: ACTIVATED (hyperlink clicked), ENTERED (mouse over a hyperlink) and EXITED (mouse moved away from a hyperlink). HyperlinkEvent method getURL obtains the URL represented by the hyperlink.
Stream-based connections are managed with Socket objects.
A ServerSocket object establishes the port where a server waits for connections from clients.
The second argument to the ServerSocket constructor is the number of connections that can wait in a queue to connect to the server. If the queue of clients is full, client connections are refused. The ServerSocket method accept waits indefinitely (i.e., blocks) for a connection from a client and returns a Socket object when a connection is established.
Socket methods getOutputStream and getInputStream get references to a Socket's
OutputStream and InputStream, respectively. Socket method close terminates a connection.
A server name and port number are specified when creating a Socket object to enable it to connect a client to the server. A failed connection attempt throws an IOException.
InetAddress method getByName returns an InetAddress object containing the host name of the
computer for which the host name or IP address is specified as an argument. InetAddress method getLocalHost returns an InetAddress object containing the host name of the local computer executing the program.
Connection-oriented transmission is like the telephone systemyou dial and are given a connection
to the telephone of the person with whom you wish to communicate. The connection is maintained for the duration of your phone call, even when you are not talking.
Connectionless transmission with datagrams is similar to mail carried via the postal service. A
large message that will not fit in one envelope can be broken into separate message pieces that are placed in separate, sequentially numbered envelopes. All the letters are then mailed at once. They could arrive in order, out of order or not at all.
DatagramPacket objects store packets of data that are to be sent or that are received by an application. DatagramSockets send and receive DatagramPackets.
The DatagramSocket constructor that takes no arguments binds the application to a port chosen
by the computer on which the program executes. The DatagramSocket constructor that takes an integer port number argument binds the application to the specified port. If a DatagramSocket constructor fails to bind the application to a port, a SocketException occurs. DatagramSocket method receive blocks (waits) until a packet arrives, then stores the packet in its argument.
DatagramPacket method getAddress returns an InetAddress object containing information about
the host computer from which the packet was sent. Method getPort returns an integer specifying the port number through which the host computer sent the DatagramPacket. Method getLength returns an integer representing the number of bytes of data in a DatagramPacket. Method geTData returns a byte array containing the data in a DatagramPacket.
[Page 1183]
The DatagramPacket constructor for a packet to be sent takes four argumentsthe byte array to
be sent, the number of bytes to be sent, the client address to which the packet will be sent and the port number where the client is waiting to receive packets.
DatagramSocket method send sends a DatagramPacket out over the network.
If an error occurs when receiving or sending a DatagramPacket, an IOException occurs.
Reading data from a Socket is a blocking callthe current thread is put in the blocked state while
it waits for the read operation to complete. Method setSoTimeout specifies that if no data is received in the given number of milliseconds, the Socket is to issue an InterruptedIOException, which the current thread can catch, then continue executing. This prevents the current thread from blocking indefinitely if no more data is available from the
Socket.
Multicast is an efficient way to send data to many clients without the overhead of broadcasting it
to every host on the Internet. Using multicast, an application can "publish" DatagramPackets to be delivered to subscriber applications. An application multicasts DatagramPackets by sending them to a multicast addressan IP address in the range from 224.0.0.0 to 239.255.255.255, reserved for multicast.
Clients that wish to receive DatagramPackets can join the multicast group that will receive the DatagramPackets published to the multicast address.
Multicast DatagramPackets are not reliablepackets are not guaranteed to reach any destination or to arrive in any particular order.
The MulticastSocket constructor takes as an argument the port to which the MulticastSocket connects to receive incoming DatagramPackets.
Method joinGroup of class MulticastSocket takes as an argument the InetAddress of the
multicast group to join. Method receive of class MulticastSocket reads an incoming
DatagramPacket from a multicast address.
