Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
626
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

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{

210// when received, display new messages in messageArea

211public void messageReceived( String from, String message )

212{

213// append message using MessageDisplayer

214SwingUtilities.invokeLater(

215new MessageDisplayer( from, message ) );

216} // end method messageReceived

217} // end MyMessageListener inner class

218

219// Displays new message by appending message to JTextArea. Should

220// be executed only in Event thread; modifies live Swing component

221private class MessageDisplayer implements Runnable

222{

223private String fromUser; // user from which message came

224private String messageBody; // body of message

225

226// MessageDisplayer constructor

227public MessageDisplayer( String from, String body )

228{

229fromUser = from; // store originating user

230messageBody = body; // store message body

231} // end MessageDisplayer constructor

232

233// display new message in messageArea

234public void run()

235{

236// append new message

237messageArea.append( "\n" + fromUser + "> " + messageBody );

238} // end method run

239} // end MessageDisplayer inner class

240} // end class ClientGUI

ClientGUI uses a MessageManager (line 40) to handle all communication with the chat server. Recall that MessageManager is an interface that enables ClientGUI to use any MessageManager implementation. Class ClientGUI also uses a MessageListener (line 41) to receive incoming messages from the MessageManager.

The ClientGUI constructor (lines 44160) takes as an argument the MessageManager for communicating with

DeitelMessengerServer. Line 48 sets the ClientGUI's MessageManager. Line 51 creates an instance of

MyMessageListener, which implements interface MessageListener. Lines 5357 create a Server menu that contains JMenuItems for connecting to and disconnecting from the chat server. Lines 6061 create an

ImageIcon for connectButton and connectMenuItem.

Lines 6465 create connectButton and connectMenuItem, each with the label "Connect" and the Icon connectIcon. Line 66 invokes method setMnemonic to set the mnemonic character for keyboard access to connectMenuItem. Line 69 creates an instance of inner class ConnectListener (declared at lines 163185), which implements interface ActionListener to handle ActionEvents from connectButton and

connectMenuItem. Lines 7071 add connectListener as an ActionListener for connectButton and connectMenuItem.

Lines 7475 create an ImageIcon for the disconnectButton and disconnectMenuItem components. Lines 7879 create disconnectButton and disconnectMenuItem, each with the label "Disconnect" and the Icon disconnectIcon. Line 80 invokes method setMnemonic to enable keyboard access to disconnectMenuItem. Lines 8384 invoke method setEnabled with a false argument on disconnectButton and disconnectMenuItem to disable these components. This prevents the user from attempting to disconnect from the server because the client is not yet connected. Line 87 creates an instance of inner class DisconnectListener (declared at lines 189204), which implements interface ActionListener to handle

ActionEvents from disconnectButton and disconnectMenuItem. Lines 8889 add disconnectListener as an ActionListener for disconnectButton and disconnectMenuItem.

Lines 9293 add connectMenuItem and disconnectMenuItem to menu Server. Lines 9698 create a JPanel and add connectButton and disconnectButton to it. Line 100 creates the textarea messageArea, in which the client displays incoming messages. Line 101 invokes method setEditable with a false argument, to disable editing. Lines 102103 invoke JTextArea methods setWrapStyleWord and setLineWrap to enable word wrapping in messageArea. If a message is longer than messageArea's width, the messageArea will wrap the text after the last word that fits on each line, making longer messages easier to read. Lines 106109 create a JPanel for the messageArea and add the messageArea to the JPanel in a JScrollPane.

[Page 1178]

Line 111 creates the inputArea JTextArea for entering new messages. Lines 112113 enable word and line

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 1180]

Executing the DeitelMessenger Client Application

To execute the DeitelMessenger client, open a command window and change directories to the location in which package com.deitel.messenger.sockets.client resides (i.e., the directory in which com is located). Then type

[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.