Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
58
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

1026

Networking

Chapter 17

 

 

 

 

 

 

Fig. 17.10 Sample outputs from the client/server Tic-Tac-Toe program (part 2 of 2).

17.9 Security and the Network

As much as we look forward to writing a great variety of powerful network-based applications, our efforts may be crimped by limitations imposed on Java because of security concerns.c Many Web browsers, such as Netscape Communicator and Microsoft Internet Explorer, by default prohibit Java applets from doing file processing on the machines on which they execute. Think about it. A Java applet is designed to be sent to your browser via an HTML document that could be downloaded from any Web server in the world. Often you will know very little about the sources of Java applets that will execute on your system. To allow these applets free rein with your files could be disastrous.

A more subtle situation occurs with limiting the machines to which executing applets can connect. To build truly collaborative applications, we would ideally like to have our applets communicate with machines almost anywhere. The Java security manager in a Web browser often restricts an applet so that it can communicate only with the machine from which it was originally downloaded.

These restrictions might seem too harsh. However, the Java Security API now provides capabilities for signed applets that will enable browsers to determine whether an applet is downloaded from a trusted source. In cases where an applet is trusted, the applet can be given additional access to the computer on which the applet is executing. The features of the Java Security API and additional networking capabilities are discussed in our text

Advanced Java 2 Platform How to Program.

17.10 DeitelMessenger Chat Server and Client

Chat rooms have become quite common on the Internet. Chat rooms provide a central location where users can chat with each other through short text messages. Each participant in a chat room can see all messages that other users post, and each user can post messages in the chat room. This section presents our capstone networking case study that integrates many of the Java networking, multithreading and Swing GUI features we have learned thus far to build an online chat system. We also introduce multicast, which enables an application to send DatagramPackets to groups of clients. After reading this section, you will be able to build significant networking applications.

Chapter 17

Networking

1027

17.10.1 DeitelMessengerServer and Supporting Classes

The DeitelMessengerServer (Fig. 17.11) is the heart of the online chat system. Chat clients can participate in a chat by connecting to the DeitelMessengerServer. Method startServer (lines 19–54) launches DeitelMessengerServer. Lines 25–26 create a ServerSocket to accept incoming network connections. Recall that the ServerSocket constructor takes as its first argument the port on which the server should listen for incoming connections. Interface SocketMessengerConstants (Fig. 17.12) defines the port value as the constant SERVER_PORT to ensure that the server and the clients uses the correct port number. Class DeitelMessengerServer implements interface SocketMessengerConstants to facilitate referencing the constants defined in that interface.

1// DeitelMessengerServer.java

2 // DeitelMessengerServer is a multi-threaded, socketand

3// packet-based chat server.

4 package com.deitel.messenger.sockets.server;

5

6 // Java core packages

7 import java.util.*;

8 import java.net.*;

9 import java.io.*;

10

11// Deitel packages

12import com.deitel.messenger.*;

13import com.deitel.messenger.sockets.*;

15public class DeitelMessengerServer implements MessageListener,

16SocketMessengerConstants {

17

18// start chat server

19public void startServer()

20{

21// create server and manage new clients

22try {

23

 

24

// create ServerSocket for incoming connections

25

ServerSocket serverSocket =

26

new ServerSocket( SERVER_PORT, 100 );

27

 

28

System.out.println( "Server listening on port " +

29

SERVER_PORT + " ..." );

30

 

31

// listen for clients constantly

32

while ( true ) {

33

 

34

// accept new client connection

35

Socket clientSocket = serverSocket.accept();

36

 

37

// create new ReceivingThread for receiving

38

// messages from client

 

 

Fig. 17.11

DeitelMessengerServer application for managing a chat room

 

(part 1 of 2).

1028

Networking

Chapter 17

 

 

39

new ReceivingThread( this, clientSocket ).start();

40

 

 

41

// print connection information

 

42

System.out.println( "Connection received from: " +

43

clientSocket.getInetAddress() );

 

44

 

 

45

} // end while

 

46

 

 

47

} // end try

 

48

 

 

49// handle exception creating server and connecting clients

50catch ( IOException ioException ) {

51ioException.printStackTrace();

52}

53

54 } // end method startServer

55

56// when new message is received, broadcast message to clients

57public void messageReceived( String from, String message )

58{

59// create String containing entire message

60String completeMessage = from + MESSAGE_SEPARATOR + message;

62// create and start MulticastSendingThread to broadcast

63// new messages to all clients

64new MulticastSendingThread(

65completeMessage.getBytes() ).start();

66}

67

68// start the server

69public static void main ( String args[] )

70{

71new DeitelMessengerServer().startServer();

72}

73}

Server listening on port 5000 ...

Connection received from: SEANSANTRY/XXX.XXX.XXX.XXX

Connection received from: PJD/XXX.XXX.XXX.XXX

Fig. 17.11 DeitelMessengerServer application for managing a chat room (part 2 of 2).

Lines 32–45 listen continuously for new client connections. Line 35 invokes method accept of class ServerSocket to wait for and accept a new client connection. Line 39 creates and starts a new ReceivingThread for the client. Class ReceivingThread (Fig. 17.14) is a Thread subclass that listens for new incoming messages from a particular client. The first argument to the ReceivingThread constructor is a MessageListener (Fig. 17.13), to which messages from the client should be delivered. Class

DeitelMessengerServer implements interface MessageListener (line 15) and therefore can pass the this reference to the ReceivingThread constructor.

Chapter 17

Networking

1029

Method messageReceived (lines 57–66) is required by interface MessageListener. When each ReceivingThread receives a new message from a client, the ReceivingThread passes the message to a MessageListener through method messageReceived. Line 60 concatenates the from String with the separator >>>

and the message body. Lines 64–65 create and start a new MulticastSendingThread to deliver completeMessage to all listening clients. Class MulticastSendingThread (Fig. 17.15) uses multicast as an efficient mechanism for sending one message to multiple clients. We discuss the details of multicasting shortly. Method main (lines 69–72) creates a new DeitelMessengerServer instance and starts the server.

Interface SocketMessengerConstants (Fig. 17.12) declares constants for use in the various classes that make up the Deitel messenger system. Classes can access these static constants either by referencing the constants through interface SocketMessengerConstants (e.g., SocketMessengerConstants.SERVER_PORT) or by implementing the interface and referencing the constants directly.

Line 9 defines the String constant MULTICAST_ADDRESS, which contains the address to which a MulticastSendingThread (Fig. 17.15) should send messages. This address is one of the addresses reserved for multicast, which we will describe soon. Line 12 defines the integer constant MULTICAST_LISTENING_PORT—the port on which clients should listen for new messages. Line 15 defines the integer constant

MULTICAST_SENDING_PORT—the port to which a MulticastSendingThread should post new messages at the MULTICAST_ADDRESS. Line 18 defines the integer constant SERVER_PORT—the port on which DeitelMessengerServer listens for incoming client connections. Line 21 defines String constant DISCONNECT_STRING, which is the String that a client sends to DeitelMessengerServer when the user wishes to leave the chat room. Line 24 defines String constant MESSAGE_SEPARATOR, which separates the user name from the message body. Line 27 specifies the maximum message size in bytes.

1// SocketMessengerConstants.java

2 // SocketMessengerConstants defines constants for the port numbers

3 // and multicast address in DeitelMessenger

4 package com.deitel.messenger.sockets;

5

6 public interface SocketMessengerConstants {

7

8// address for multicast datagrams

9 public static final String MULTICAST_ADDRESS = "230.0.0.1";

10

11// port for listening for multicast datagrams

12public static final int MULTICAST_LISTENING_PORT = 5555;

14// port for sending multicast datagrams

15public static final int MULTICAST_SENDING_PORT = 5554;

17// port for Socket connections to DeitelMessengerServer

18public static final int SERVER_PORT = 5000;

Fig. 17.12 SocketMessengerConstants declares constants for use throughout the DeitelMessengerServer and DeitelMessenger applications (part 1 of 2).

1030

Networking

Chapter 17

19

20// String that indicates disconnect

21public static final String DISCONNECT_STRING = "DISCONNECT";

23// String that separates the user name from the message body

24public static final String MESSAGE_SEPARATOR = ">>>";

25

26// message size (in bytes)

27public static final int MESSAGE_SIZE = 512;

28}

Fig. 17.12 SocketMessengerConstants declares constants for use throughout the DeitelMessengerServer and DeitelMessenger applications (part 2 of 2).

Many different classes in the Deitel messenger system receive messages. For example, DeitelMessengerServer receives messages from clients and delivers those messages to all chat room participants. As we will see, the user interface for each client also receives messages and displays those messages to the users. Each of the classes that receives messages implements interface MessageListener (Fig. 17.13). The interface declares method messageReceived, which allows an implementing class to receive chat messages. Method messageReceived takes two String arguments representing the name of the user who sent the message and the message body, respectively.

DeitelMessengerServer uses instances of class ReceivingThread

(Fig. 17.14) to listen for new messages from each client. Class ReceivingThread extends class Thread. This enables DeitelMessengerServer to create an object of class ReceivingThread for each client, to handle messages from multiple clients at once. When DeitelMessengerServer receives a new client connection,

DeitelMessengerServer creates a new ReceivingThread for the client, then continues listening for new client connections. The ReceivingThread listens for new messages from a single client and passes those messages back to the DeitelMessengerServer through method messageReceived.

1// MessageListener.java

2 // MessageListener is an interface for classes that wish to 3 // receive new chat messages.

4 package com.deitel.messenger;

5

6 public interface MessageListener {

7

8// receive new chat message

9 public void messageReceived( String from, String message );

10 }

Fig. 17.13 MessageListener interface that defines method messageReceived for receiving new chat messages.

Chapter 17

Networking

1031

1// ReceivingThread.java

2 // ReceivingThread is a Thread that listens for messages 3 // from a particular client and delivers messages to a

4// MessageListener.

5 package com.deitel.messenger.sockets.server;

6

7 // Java core packages

8 import java.io.*;

9import java.net.*;

10 import java.util.StringTokenizer;

11

12// Deitel packages

13import com.deitel.messenger.*;

14import com.deitel.messenger.sockets.*;

16public class ReceivingThread extends Thread implements

17SocketMessengerConstants {

18

19private BufferedReader input;

20private MessageListener messageListener;

21private boolean keepListening = true;

22

23// ReceivingThread constructor

24public ReceivingThread( MessageListener listener,

25Socket clientSocket )

26{

27// invoke superclass constructor to name Thread

28super( "ReceivingThread: " + clientSocket );

29

30// set listener to which new messages should be sent

31messageListener = listener;

32

33// set timeout for reading from clientSocket and create

34// BufferedReader for reading incoming messages

35try {

36

clientSocket.setSoTimeout( 5000 );

37

 

38

input = new BufferedReader( new InputStreamReader(

39

clientSocket.getInputStream() ) );

40

}

41

 

42// handle exception creating BufferedReader

43catch ( IOException ioException ) {

44ioException.printStackTrace();

45}

46

47 } // end ReceivingThread constructor

48

49// listen for new messages and deliver them to MessageListener

50public void run()

51{

52String message;

Fig. 17.14 ReceivingThread for listening for new messages from DeitelMessengerServer clients in separate Threads (part 1 of 3).

1032

Networking

Chapter 17

53

54// listen for messages until stopped

55while ( keepListening ) {

56

 

57

// read message from BufferedReader

58

try {

59

message = input.readLine();

60

}

61

 

62

// handle exception if read times out

63

catch ( InterruptedIOException interruptedIOException ) {

64

 

65

// continue to next iteration to keep listening

66

continue;

67

}

68

 

69

// handle exception reading message

70

catch ( IOException ioException ) {

71

ioException.printStackTrace();

72

break;

73

}

74

 

75

// ensure non-null message

76

if ( message != null ) {

77

 

78

// tokenize message to retrieve user name

79

// and message body

80

StringTokenizer tokenizer =

81

new StringTokenizer( message, MESSAGE_SEPARATOR );

82

 

83

// ignore messages that do not contain a user

84

// name and message body

85

if ( tokenizer.countTokens() == 2 ) {

86

 

87

// send message to MessageListener

88

messageListener.messageReceived(

89

tokenizer.nextToken(), // user name

90

tokenizer.nextToken() ); // message body

91

}

92

 

93

else

94

 

95

// if disconnect message received, stop listening

96

if ( message.equalsIgnoreCase( MESSAGE_SEPARATOR +

97

DISCONNECT_STRING ) ) {

98

 

99

stopListening();

100

}

101

 

102

} // end if

103

 

104

} // end while

 

 

Fig. 17.14 ReceivingThread for listening for new messages from DeitelMessengerServer clients in separate Threads (part 2 of 3).

Chapter 17

Networking

1033

105

106// close BufferedReader (also closes Socket)

107try {

108input.close();

109}

110

111// handle exception closing BufferedReader

112catch ( IOException ioException ) {

113ioException.printStackTrace();

114}

115

116 } // end method run

117

118// stop listening for incoming messages

119public void stopListening()

120{

121keepListening = false;

122}

123}

Fig. 17.14 ReceivingThread for listening for new messages from DeitelMessengerServer clients in separate Threads (part 3 of 3).

The ReceivingThread constructor (lines 24–47) takes as its first argument a

MessageListener. The ReceivingThread will deliver new messages to this MessageListener by invoking method messageReceived of interface MessageListener. The Socket argument to the ReceivingThread constructor is the connection to a particular client. Line 28 invokes the Thread constructor to provide a unique name for each ReceivingThread instance. Naming the ReceivingThread this way can be useful when debugging the application. Line 31 sets the MessageListener to which the ReceivingThread should deliver new messages. Line 36 invokes method setSoTimeout of class Socket with an integer argument of 5000 milliseconds. Reading data from a Socket is a blocking call—the current thread is put in the blocked state (Fig. 15.1) while the thread waits for the read operation to complete. Method setSoTimeout specifies that, if no data is received in the given number of milliseconds, the Socket should issue an InterruptedIOException, which the current thread can catch, then continue executing. This technique prevents the current thread from deadlocking if no more data is available from the Socket. Lines 38–39 create a new BufferedReader for the clientSocket’s InputStream. The ReceivingThread uses this BufferedReader to read new messages from the client.

Method run (lines 50–116) listens continuously for new messages from the client. Lines 55–104 loop as long as the boolean variable keepListening is true. Line 59 invokes BufferedReader method readLine to read a line of data from the client. If more than 5000 milliseconds pass without reading any data, method readLine throws an InterruptedIOException, which indicates that the timeout set on line 36 has expired. Line 66 uses keyword continue to go to the next iteration of the while loop to continue listening for messages. Lines 70–73 catch an IOException, which indicates a more severe problem from method readLine. Line 71 prints a stack trace to aid in debugging the application, and line 72 uses keyword break to terminate the while loop.

1034

Networking

Chapter 17

When a client sends a message to the server, the client separates the user’s name from the message body with the String MESSAGE_SEPARATOR. If there are no exceptions thrown when reading data from the client and the message is not null (line 76), lines 80– 81 create a new StringTokenizer. This StringTokenizer separates each message into two tokens delimited by MESSAGE_SEPARATOR. The first token is the sender’s user name; the second token is the message. Line 85 checks for the proper number of tokens, and lines 88–90 invoke method messageReceived of interface MessageListener to deliver the new message to the registered MessageListener. If the StringTokenizer does not produce two tokens, lines 96–97 check the message to see whether it matches the constant DISCONNECT_STRING, which would indicate that the user wishes to leave the chat room. If the Strings match, line 99 invokes ReceivingThread method stopListening to terminate the ReceivingThread.

Method stopListening (lines 119–122) sets boolean variable keepListening to false. This causes the while loop condition on line 55 to fail and causes the ReceivingThread to close the client Socket (line 108). Then, method run returns, which terminates the ReceivingThread’s execution.

MulticastSendingThread (Fig. 17.15) delivers DatagramPackets containing chat messages to a group of clients. Multicast is an efficient way to send data to many clients without the overhead of broadcasting that data to every host on the Internet. To understand multicast, let us look at a real-world analogy—the relationship between a magazine publisher and that magazine’s subscribers. The magazine publisher produces a magazine and provides the magazine to a distributor. Customers interested in that magazine obtain a subscription and begin receiving the magazine in the mail from the distributor. This communication is quite different from a television broadcast. When a television station produces a television program, the station broadcasts that television show throughout a geographical region or perhaps throughout the world by using satellites. Broadcasting a television show for 10,000 viewers is no more expensive to the television station than broadcasting a television show for 100 viewers—the radio signal carrying the broadcast reaches a wide area. However, printing and delivering a magazine to 10,000 readers would be much more expensive than printing and delivering the magazine to 100 readers. Most magazine publishers could not stay in business if they had to broadcast their magazines to everyone, so magazine publishers multicast their magazines to a group of subscribers instead.

Using multicast, an application can “publish” DatagramPackets to be delivered to other applications—the “subscribers.” An application multicasts DatagramPackets by sending the DatagramPackets to a multicast address, which is an IP address reserved for multicast in the range from 224.0.0.0 to 239.255.255.255. Clients that wish to receive these DatagramPackets can connect to the appropriate multicast address to join the group of subscribers—the multicast group. When an application sends a DatagramPacket to the multicast address, each client in the multicast group receives the DatagramPacket. Multicast DatagramPackets, like unicast DatagramPackets (Fig. 17.7), are not reliable—packets are not guaranteed to reach any destination. Also, the order in which the particular clients receive the datagrams is not guaranteed.

Class MulticastSendingThread extends class Thread to enable DeitelMessengerServer to send multicast messages in a separate thread. Each time DeitelMessengerServer needs to multicast a message, the server creates a new MulticastSendingThread with the contents of the message and starts the thread.

Chapter 17

Networking

1035

The MulticastSendingThread constructor (lines 20–26) takes as an argument an array of bytes containing the message.

1// MulticastSendingThread.java

2 // MulticastSendingThread is a Thread that broadcasts a chat

3// message using a multicast datagram.

4 package com.deitel.messenger.sockets.server;

5

6 // Java core packages

7 import java.io.*;

8 import java.net.*;

9

10// Deitel packages

11import com.deitel.messenger.sockets.*;

13public class MulticastSendingThread extends Thread

14implements SocketMessengerConstants {

15

16// message data

17private byte[] messageBytes;

19// MulticastSendingThread constructor

20public MulticastSendingThread( byte[] bytes )

21{

22// invoke superclass constructor to name Thread

23super( "MulticastSendingThread" );

24

25messageBytes = bytes;

26}

27

28// deliver message to MULTICAST_ADDRESS over DatagramSocket

29public void run()

30{

31// deliver message

32try {

33

 

34

// create DatagramSocket for sending message

35

DatagramSocket socket =

36

new DatagramSocket( MULTICAST_SENDING_PORT );

37

 

38

// use InetAddress reserved for multicast group

39

InetAddress group = InetAddress.getByName(

40

MULTICAST_ADDRESS );

41

 

42

// create DatagramPacket containing message

43

DatagramPacket packet = new DatagramPacket(

44

messageBytes, messageBytes.length, group,

45

MULTICAST_LISTENING_PORT );

46

 

47

// send packet to multicast group and close socket

48

socket.send( packet );

 

 

Fig. 17.15

MulticastSendingThread for delivering outgoing messages to a

 

multicast group via DatagramPackets.

1036

Networking

Chapter 17

49socket.close();

50}

51

52// handle exception delivering message

53catch ( IOException ioException ) {

54ioException.printStackTrace();

55}

56

57} // end method run

58}

Fig. 17.15 MulticastSendingThread for delivering outgoing messages to a multicast group via DatagramPackets.

Method run (lines 29–57) delivers the message to the multicast address. Lines 35–36 create a new DatagramSocket. Recall from the packet-networking example that we used DatagramSockets to send unicast DatagramPackets—packets sent from one host directly to another host. Delivering DatagramPackets by using multicast is exactly the same, except the address to which the DatagramPackets are sent is a multicast address in the range from 224.0.0.0 to 239.255.255.255. Lines 39–40 create an InetAddress object for the multicast address, which is defined as a constant in interface

SocketMessengerConstants. Lines 43–45 create the DatagramPacket containing the message. The first argument to the DatagramPacket constructor is the byte array containing the message. The second argument is the length of the byte array. The third argument specifies the InetAddress to which the packet should be sent, and the last argument specifies the port number through which the packet should be delivered to the multicast address. Line 48 invokes method send of class DatagramSocket to send the DatagramPacket. When the DatagramPacket is delivered to the multicast address, all clients listening to that multicast address on the proper port receive the DatagramPacket. Line 49 closes the DatagramSocket, and the run method returns, terminating the MulticastSendingThread.

17.10.2 DeitelMessenger Client and Supporting Classes

The client for the DeitelMessengerServer consists of several pieces. A class that implements interface MessageManager (Fig. 17.16) manages communication with the server. A Thread subclass listens for messages at DeitelMessengerServer’s multicast address. Another Thread subclass sends messages from the client to DeitelMessengerServer. A JFrame subclass provides a GUI for the client.

Interface MessageManager (Fig. 17.16) defines methods for managing communication with DeitelMessengerServer. We define this interface to abstract the base functionality a client needs to interact with a chat server from the underlying communication mechanism needed to communicate with that chat server. This abstraction enables us to provide MessageManager implementations that use other network protocols to implement the communication details. For example, if we want to connect to a different chat server that does not use multicast DatagramPackets, we could implement the MessageManager interface with the appropriate network protocols for this alternate messaging server. We would not need to modify any other code in the client, because the

Chapter 17

Networking

1037

other components of the client refer only to interface MessageManager, and not to some particular MessageManager implementation. Likewise, the MessageManager interface methods refer to other components of the client only through interface MessageListener. Therefore, other components of the client can change without requiring changes in the MessageManager or its implementations.Method connect (line 10) connects MessageManager to DeitelMessengerServer and routes incoming messages to the appropriate MessageListener. Method disconnect (line 14) disconnects the MessageManager from the DeitelMessengerServer and stops delivering messages to the given MessageListener. Method sendMessage (line 17) sends a new message to DeitelMessengerServer.

Class SocketMessageManager (Fig. 17.17) implements interface MessageManager (line 16), using Sockets and MulticastSockets to communicate with DeitelMessengerServer and receive incoming messages. Line 20 declares the Socket that SocketMessageManager uses to connect and send messages to DeitelMessengerServer. Line 26 declares the PacketReceivingThread (Fig. 17.19) that listens for new incoming messages. The boolean flag connected (line 29) indicates whether the SocketMessageManager is connected to DeitelMessengerServer. \

1// MessageManager.java

2 // MessageManager is an interface for objects capable of managing 3 // communications with a message server.

4 package com.deitel.messenger;

5

6 public interface MessageManager {

7

8 // connect to message server and route incoming messages

9// to given MessageListener

10 public void connect( MessageListener listener );

11

12// disconnect from message server and stop routing

13// incoming messages to given MessageListener

14public void disconnect( MessageListener listener );

16// send message to message server

17public void sendMessage( String from, String message );

18}

Fig. 17.16 MessageManager interface that defines methods for communicating with a DeitelMessengerServer.

1// SocketMessageManager.java

2 // SocketMessageManager is a MessageManager implementation for

3 // communicating with a DeitelMessengerServer using Sockets

4// and MulticastSockets.

5 package com.deitel.messenger.sockets.client;

6

Fig. 17.17 SocketMessageManager implementation of interface MessageManager for communicating via Sockets and multicast

DatagramPackets (part 1 of 4).

1038

Networking

Chapter 17

7 // Java core packages

8 import java.util.*;

9 import java.net.*;

10 import java.io.*;

11

12// Deitel packages

13import com.deitel.messenger.*;

14import com.deitel.messenger.sockets.*;

16public class SocketMessageManager implements MessageManager,

17SocketMessengerConstants {

18

19// Socket for outgoing messages

20private Socket clientSocket;

21

22// DeitelMessengerServer address

23private String serverAddress;

24

25// Thread for receiving multicast messages

26private PacketReceivingThread receivingThread;

28// flag indicating connection status

29private boolean connected = false;

31// SocketMessageManager constructor

32public SocketMessageManager( String address )

33{

34serverAddress = address;

35}

36

37// connect to server and send messages to given MessageListener

38public void connect( MessageListener listener )

39{

40// if already connected, return immediately

41if ( connected )

42

return;

43

 

44// open Socket connection to DeitelMessengerServer

45try {

46

clientSocket = new Socket(

47

InetAddress.getByName( serverAddress ), SERVER_PORT );

48

 

49

// create Thread for receiving incoming messages

50

receivingThread = new PacketReceivingThread( listener );

51

receivingThread.start();

52

 

53

// update connected flag

54

connected = true;

55

 

56

} // end try

57

 

Fig. 17.17 SocketMessageManager implementation of interface MessageManager for communicating via Sockets and multicast

DatagramPackets (part 2 of 4).

Chapter 17

Networking

1039

58// handle exception connecting to server

59catch ( IOException ioException ) {

60ioException.printStackTrace();

61}

62

63 } // end method connect

64

65// disconnect from server and unregister given MessageListener

66public void disconnect( MessageListener listener )

67{

68// if not connected, return immediately

69if ( !connected )

70

return;

71

 

72// stop listening thread and disconnect from server

73try {

74

 

75

// notify server that client is disconnecting

76

Thread disconnectThread = new SendingThread(

77

clientSocket, "", DISCONNECT_STRING );

78

disconnectThread.start();

79

 

80

// wait 10 seconds for disconnect message to be sent

81

disconnectThread.join( 10000 );

82

 

83

// stop receivingThread and remove given MessageListener

84

receivingThread.stopListening();

85

 

86

// close outgoing Socket

87

clientSocket.close();

88

 

89

} // end try

90

 

91// handle exception disconnecting from server

92catch ( IOException ioException ) {

93ioException.printStackTrace();

94}

95

96// handle exception joining disconnectThread

97catch ( InterruptedException interruptedException ) {

98interruptedException.printStackTrace();

99}

100

101// update connected flag

102connected = false;

103

104 } // end method disconnect

105

106// send message to server

107public void sendMessage( String from, String message )

108{

Fig. 17.17 SocketMessageManager implementation of interface MessageManager for communicating via Sockets and multicast

DatagramPackets (part 3 of 4).

1040

Networking

Chapter 17

109// if not connected, return immediately

110if ( !connected )

111

return;

112

 

113// create and start new SendingThread to deliver message

114new SendingThread( clientSocket, from, message).start();

115}

116}

Fig. 17.17 SocketMessageManager implementation of interface MessageManager for communicating via Sockets and multicast

DatagramPackets (part 4 of 4).

The SocketMessageManager constructor (lines 32–35) receives the address of the DeitelMessengerServer to which SocketMessageManager should connect. Method connect (lines 38–63) connects SocketMessageManager to

DeitelMessengerServer. If SocketMessageManager was connected previously, line 42 returns from method connect. Lines 46–47 create a new Socket to communicate with DeitelMessengerServer. Line 47 creates an InetAddress object for the server’s address and uses the constant SERVER_PORT to specify the port on which the client should connect. Line 50 creates a new PacketReceivingThread, which listens for incoming multicast messages from DeitelMessengerServer. Line 51 starts

PacketReceivingThread. Line 54 updates boolean variable connected to indicate that SocketMessageManager is connected to the server.

Method disconnect (lines 66–104) terminates the SocketMessageManager’s connection to DeitelMessengerServer. If SocketMessageManager is not connected, line 70 returns from method disconnect. Lines 76–77 create a new SendingThread (Fig. 17.18) to send DISCONNECT_STRING to DeitelMessengerServer. Class SendingThread delivers a message to DeitelMessengerServer over the SocketMessageManager’s Socket connection. Line 78 starts the SendingThread to deliver the message. Line 81 invokes SendingThread method join (inherited from Thread) to wait for the disconnect message to be delivered. The integer argument 10000 specifies that the current thread should wait only 10 seconds to join the SendingThread before continuing. Once the disconnect message has been delivered, line 84 invokes method stopListening of class PacketReceivingThread to stop receiving incoming chat messages. Line 87 closes the Socket connection to DeitelMessengerServer.

Method sendMessage (lines 107–115) sends an outgoing message to DeitelMessengerServer. If SocketMessageManager is not connected, line 111 returns from method sendMessage. Line 114 creates and starts a new SendingThread instance (Fig. 17.18) to deliver the new message in a separate thread of execution.

Class SendingThread (Fig. 17.18) extends class Thread to deliver outgoing messages to the DeitelMessengerServer in a separate thread of execution. SendingThread’s constructor (lines 21–31) takes as arguments the Socket over which to send the message, the userName from whom the message came and the message body. Line 30 concatenates userName, MESSAGE_SEPARATOR and message to build messageToSend. Constant MESSAGE_SEPARATOR enables the message recipient to parse the message into two parts—the sending user’s name and the message body—by using a

StringTokenizer.

Chapter 17

Networking

1041

1// SendingThread.java

2 // SendingThread sends a message to the chat server in a

3// separate Thread.

4 package com.deitel.messenger.sockets.client;

5

6 // Java core packages

7 import java.io.*;

8 import java.net.*;

9

10// Deitel packages

11import com.deitel.messenger.sockets.*;

13public class SendingThread extends Thread

14implements SocketMessengerConstants {

16// Socket over which to send message

17private Socket clientSocket;

18private String messageToSend;

19

20// SendingThread constructor

21public SendingThread( Socket socket, String userName,

22String message )

23{

24// invoke superclass constructor to name Thread

25super( "SendingThread: " + socket );

26

27 clientSocket = socket;

28

29// build the message to be sent

30messageToSend = userName + MESSAGE_SEPARATOR + message;

31}

32

33// send message and exit Thread

34public void run()

35{

36// send message and flush PrintWriter

37try {

38

PrintWriter writer =

39

new PrintWriter( clientSocket.getOutputStream() );

40

writer.println( messageToSend );

41writer.flush();

42}

43

44// handle exception sending message

45catch ( IOException ioException ) {

46ioException.printStackTrace();

47}

48

49} // end method run

50}

Fig. 17.18 SendingThread for delivering outgoing messages to

DeitelMessengerServer.

1042

Networking

Chapter 17

Method run (lines 34–49) delivers the complete message to DeitelMessengerServer, using the Socket provided to the SendingThread constructor. Lines 38–39 create a new PrintWriter for the clientSocket’s OutputStream. Line 40 invokes method println of class PrintWriter to send the message. Line 41 invokes method flush of class PrintWriter to ensure that the message is sent immediately. Note that class SendingThread does not close the clientSocket. Class

SocketMessageManager uses a new instance of class SendingThread for each message the client sends, so the clientSocket must remain open until the user disconnects from DeitelMessengerServer.

Class PacketReceivingThread extends class Thread to enable SocketMessageManager to listen for incoming messages in a separate thread of execution. Line 19 declares the MessageListener to which PacketReceivingThread will deliver incoming messages. Line 22 declares a MulticastSocket, which enables

PacketReceivingThread to receive multicast DatagramPackets. Line 25 declares an InetAddress reference for the multicast address to which DeitelMessengerServer posts new chat messages. The MulticastSocket connects to this InetAddress to listen for incoming chat messages.

1// PacketReceivingThread.java

2 // PacketReceivingThread listens for DatagramPackets containing

3// messages from a DeitelMessengerServer.

4 package com.deitel.messenger.sockets.client;

5

6 // Java core packages

7 import java.io.*;

8 import java.net.*;

9 import java.util.*;

10

11// Deitel packages

12import com.deitel.messenger.*;

13import com.deitel.messenger.sockets.*;

15public class PacketReceivingThread extends Thread

16implements SocketMessengerConstants {

17

18// MessageListener to whom messages should be delivered

19private MessageListener messageListener;

20

21// MulticastSocket for receiving broadcast messages

22private MulticastSocket multicastSocket;

23

24// InetAddress of group for messages

25private InetAddress multicastGroup;

27// flag for terminating PacketReceivingThread

28private boolean keepListening = true;

29

Fig. 17.19 PacketReceivingThread for listening for new multicast messages from DeitelMessengerServer in a separate Thread (part 1 of 4).

Chapter 17

Networking

1043

30// PacketReceivingThread constructor

31public PacketReceivingThread( MessageListener listener )

32{

33// invoke superclass constructor to name Thread

34super( "PacketReceivingThread" );

35

36// set MessageListener

37messageListener = listener;

39// connect MulticastSocket to multicast address and port

40try {

41

multicastSocket =

42

new MulticastSocket( MULTICAST_LISTENING_PORT );

43

 

44

multicastGroup =

45

InetAddress.getByName( MULTICAST_ADDRESS );

46

 

47

// join multicast group to receive messages

48

multicastSocket.joinGroup( multicastGroup );

49

 

50

// set 5 second time-out when waiting for new packets

51multicastSocket.setSoTimeout( 5000 );

52}

53

54// handle exception connecting to multicast address

55catch ( IOException ioException ) {

56ioException.printStackTrace();

57}

58

59 } // end PacketReceivingThread constructor

60

61// listen for messages from multicast group

62public void run()

63{

64// listen for messages until stopped

65while ( keepListening ) {

66

 

67

// create buffer for incoming message

68

byte[] buffer = new byte[ MESSAGE_SIZE ];

69

 

70

// create DatagramPacket for incoming message

71

DatagramPacket packet = new DatagramPacket( buffer,

72

MESSAGE_SIZE );

73

 

74

// receive new DatagramPacket (blocking call)

75

try {

76

multicastSocket.receive( packet );

77

}

78

 

79

// handle exception when receive times out

80

catch ( InterruptedIOException interruptedIOException ) {

81

 

Fig. 17.19 PacketReceivingThread for listening for new multicast messages from DeitelMessengerServer in a separate Thread (part 2 of 4).

1044

Networking

Chapter 17

 

 

82

// continue to next iteration to keep listening

83

continue;

 

84

}

 

85

 

 

86

// handle exception reading packet from multicast group

87

catch ( IOException ioException ) {

88

ioException.printStackTrace();

 

89

break;

 

90

}

 

91

 

 

92

// put message data in a String

 

93

String message = new String( packet.getData() );

94

 

 

95

// ensure non-null message

 

96

if ( message != null ) {

 

97

 

 

98

// trim extra whitespace from end of message

99

message = message.trim();

 

100

 

 

101

// tokenize message to retrieve user name

102

// and message body

 

103

StringTokenizer tokenizer =

 

104

new StringTokenizer( message, MESSAGE_SEPARATOR );

105

 

 

106

// ignore messages that do not contain a user

107

// name and message body

 

108

if ( tokenizer.countTokens() == 2 ) {

109

 

 

110

// send message to MessageListener

111

messageListener.messageReceived(

112

tokenizer.nextToken(),

// user name

113

tokenizer.nextToken() ); // message body

114

}

 

115

 

 

116

} // end if

 

117

 

 

118

} // end while

 

119

 

 

120// leave multicast group and close MulticastSocket

121try {

122 multicastSocket.leaveGroup( multicastGroup );

123multicastSocket.close();

124}

125

126// handle exception reading packet from multicast group

127catch ( IOException ioException ) {

128ioException.printStackTrace();

129}

130

131 } // end method run

132

Fig. 17.19 PacketReceivingThread for listening for new multicast messages from DeitelMessengerServer in a separate Thread (part 3 of 4).

Chapter 17

Networking

1045

133// stop listening for new messages

134public void stopListening()

135{

136// terminate Thread

137keepListening = false;

138}

139}

Fig. 17.19 PacketReceivingThread for listening for new multicast messages from DeitelMessengerServer in a separate Thread (part 4 of 4).

The PacketReceivingThread constructor (lines 31–59) takes as an argument the MessageListener to which the PacketReceivingThread should deliver incoming messages. Recall that interface MessageListener defines a single method messageReceived. When the PacketReceivingThread receives a new chat message over the MulticastSocket, PacketReceivingThread invokes method messageReceived to deliver the new message to the MessageListener.

Lines 41–42 create a new MulticastSocket and pass to the MulticastSocket constructor the constant MULTICAST_LISTENING_PORT from interface SocketMessengerConstants. This argument specifies the port on which the MulticastSocket should listen for incoming chat messages. Lines 44–45 create an InetAddress object for the MULTICAST_ADDRESS, to which DeitelMessengerServer multicasts new chat messages. Line 48 invokes method joinGroup of class MulticastSocket to register the MulticastSocket to receive messages sent to MULTICAST_ADDRESS. Line 51 invokes MulticastSocket method setSoTime-out to specify that, if no data is received in 5000 milliseconds, the MulticastSocket should issue an InterruptedIOException, which the current thread can catch, then continue executing. This approach prevents PacketReceivingThread from deadlocking when waiting for incoming data. Also, if the MulticastSocket did not ever time out, the while loop would not be able to check the keepListening variable and would therefore prevent

PacketReceivingThread from stopping if keepListening were set to false. Method run (lines 62–131) listens for incoming multicast messages. Line 68 creates

a byte array in which to store the incoming DatagramPacket data. Lines 71–72 create a DatagramPacket to store the incoming message. Line 76 invokes method receive of class MulticastSocket with the DatagramPacket packet as an argument. This is a blocking call that reads an incoming packet from the multicast address. If 5000 milliseconds pass without receipt of a packet, method receive throws an InterruptedIOException, because we previously set a 5000 millisecond time-out (line 51). Line 83 uses keyword continue to proceed to the next while loop iteration to continue listening for incoming messages. For other IOExceptions, line 89 breaks the while loop to terminate the PacketReceivingThread.

Line 93 invokes method getData of class DatagramPacket to retrieve the message data. Line 99 invokes method trim of class String to remove extra whitespace from the end of the message. Recall that DatagramPackets are a fixed size—512 bytes in this example—so, if the message is shorter than 512 bytes, there will be extra whitespace after the message. Lines 103–104 create a StringTokenizer to separate the message body from the name of the user who sent the message. Line 108 checks for the correct number of tokens. Lines 111–113 invoke method messageReceived of inter-

1046

Networking

Chapter 17

face MessageListener to deliver the incoming message to the PacketReceivingThread’s MessageListener.

If the program invokes method stopListening (lines 134–138), the while loop in method run (lines 62–118) terminates. Line 122 invokes method leaveGroup of class MulticastSocket to stop receiving messages from the multicast address. Line 123 invokes method close of class MulticastSocket to close the MulticastSocket. PacketReceivingThread then terminates when method run returns.

Class ClientGUI (Fig. 17.20) extends class JFrame to create a GUI for a user to send and receive chat messages. The GUI consists of a JTextArea for displaying incoming messages (line 22), a JTextArea for entering new messages (line 23), JButtons and JMenuItems for connecting to and disconnecting from the server (lines 26–29) and a JButton for sending messages (line 32). The GUI also contains a JLabel that displays whether the client is connected or disconnected.

1// ClientGUI.java

2 // ClientGUI provides a user interface for sending and receiving 3 // messages to and from the DeitelMessengerServer.

4 package com.deitel.messenger;

5

6 // Java core packages

7 import java.io.*;

8 import java.net.*;

9import java.awt.*;

10 import java.awt.event.*;

11

12// Java standard extensions

13import javax.swing.*;

14import javax.swing.border.*;

16 public class ClientGUI extends JFrame {

17

18// JMenu for connecting/disconnecting server

19private JMenu serverMenu;

20

21// JTextAreas for displaying and inputting messages

22private JTextArea messageArea;

23private JTextArea inputArea;

24

25// JButtons and JMenuItems for connecting and disconnecting

26private JButton connectButton;

27private JMenuItem connectMenuItem;

28private JButton disconnectButton;

29private JMenuItem disconnectMenuItem;

30

31// JButton for sending messages

32private JButton sendButton;

33

34// JLabel for displaying connection status

35private JLabel statusBar;

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 1 of 6).

Chapter 17

Networking

1047

36

37// userName to add to outgoing messages

38private String userName;

39

40// MessageManager for communicating with server

41private MessageManager messageManager;

42

43// MessageListener for receiving incoming messages

44private MessageListener messageListener;

45

46// ClientGUI constructor

47public ClientGUI( MessageManager manager )

48{

49super( "Deitel Messenger" );

50

51// set the MessageManager

52messageManager = manager;

54// create MyMessageListener for receiving messages

55messageListener = new MyMessageListener();

56

57// create File JMenu

58serverMenu = new JMenu ( "Server" );

59serverMenu.setMnemonic( 'S' );

60JMenuBar menuBar = new JMenuBar();

61menuBar.add( serverMenu );

62setJMenuBar( menuBar );

63

64// create ImageIcon for connect buttons

65Icon connectIcon = new ImageIcon(

66

getClass().getResource( "images/Connect.gif" ) );

67

 

68// create connectButton and connectMenuItem

69connectButton = new JButton( "Connect", connectIcon );

70connectMenuItem = new JMenuItem( "Connect", connectIcon );

71connectMenuItem.setMnemonic( 'C' );

72

73// create ConnectListener for connect buttons

74ActionListener connectListener = new ConnectListener();

75connectButton.addActionListener( connectListener );

76connectMenuItem.addActionListener( connectListener );

78// create ImageIcon for disconnect buttons

79Icon disconnectIcon = new ImageIcon(

80

getClass().getResource( "images/Disconnect.gif" ) );

81

 

82// create disconnectButton and disconnectMenuItem

83disconnectButton = new JButton( "Disconnect",

84disconnectIcon );

85disconnectMenuItem = new JMenuItem( "Disconnect",

86disconnectIcon );

87disconnectMenuItem.setMnemonic( 'D' );

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 2 of 6).

1048

Networking

Chapter 17

88

89// disable disconnect buttons

90disconnectButton.setEnabled( false );

91disconnectMenuItem.setEnabled( false );

93// create DisconnectListener for disconnect buttons

94ActionListener disconnectListener =

95new DisconnectListener();

96disconnectButton.addActionListener( disconnectListener );

97disconnectMenuItem.addActionListener( disconnectListener );

99 // add connect and disconnect JMenuItems to fileMenu

100serverMenu.add( connectMenuItem );

101serverMenu.add( disconnectMenuItem );

103// add connect and disconnect JButtons to buttonPanel

104JPanel buttonPanel = new JPanel();

105buttonPanel.add( connectButton );

106buttonPanel.add( disconnectButton );

107

108// create JTextArea for displaying messages

109messageArea = new JTextArea();

110

111// disable editing and wrap words at end of line

112messageArea.setEditable( false );

113messageArea.setWrapStyleWord( true );

114messageArea.setLineWrap( true );

115

116// put messageArea in JScrollPane to enable scrolling

117JPanel messagePanel = new JPanel();

118messagePanel.setLayout( new BorderLayout( 10, 10 ) );

119messagePanel.add( new JScrollPane( messageArea ),

120

BorderLayout.CENTER );

121

 

122// create JTextArea for entering new messages

123inputArea = new JTextArea( 4, 20 );

124inputArea.setWrapStyleWord( true );

125inputArea.setLineWrap( true );

126inputArea.setEditable( false );

127

128// create Icon for sendButton

129Icon sendIcon = new ImageIcon(

130 getClass().getResource( "images/Send.gif" ) );

131

132// create sendButton and disable it

133sendButton = new JButton( "Send", sendIcon );

134sendButton.setEnabled( false );

135

136// create ActionListener for sendButton

137sendButton.addActionListener(

138 new ActionListener() {

139

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 3 of 6).

Chapter 17

Networking

1049

 

 

140

// send new message when user activates sendButton

141

public void actionPerformed( ActionEvent event )

 

142

{

 

143

messageManager.sendMessage( userName,

 

144

inputArea.getText());

 

145

 

 

146

// clear inputArea

 

147

inputArea.setText( "" );

 

148

}

 

149} // end ActionListener

150);

151

152// lay out inputArea and sendButton in BoxLayout and

153// add Box to messagePanel

154Box box = new Box( BoxLayout.X_AXIS );

155box.add( new JScrollPane( inputArea ) );

156box.add( sendButton );

157messagePanel.add( box, BorderLayout.SOUTH );

158

159// create JLabel for statusBar with a recessed border

160statusBar = new JLabel( "Not Connected" );

161statusBar.setBorder(

162 new BevelBorder( BevelBorder.LOWERED ) );

163

164// lay out components in JFrame

165Container container = getContentPane();

166container.add( buttonPanel, BorderLayout.NORTH );

167container.add( messagePanel, BorderLayout.CENTER );

168container.add( statusBar, BorderLayout.SOUTH );

169

170// add WindowListener to disconnect when user quits

171addWindowListener (

172

new WindowAdapter () {

173

 

174

// disconnect from server and exit application

175

public void windowClosing ( WindowEvent event )

176

{

177

messageManager.disconnect( messageListener );

178

System.exit( 0 );

179

}

180}

181);

183 } // end ClientGUI constructor

184

185// ConnectListener listens for user requests to connect to

186// DeitelMessengerSever

187private class ConnectListener implements ActionListener {

189// connect to server and enable/disable GUI components

190public void actionPerformed( ActionEvent event )

191{

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 4 of 6).

1050

Networking

Chapter 17

 

 

 

192

// connect to server and route messages to

 

193

// messageListener

 

194

messageManager.connect( messageListener );

 

195

 

 

196

// prompt for userName

 

197

userName = JOptionPane.showInputDialog(

 

198

ClientGUI.this, "Enter user name:" );

 

199

 

 

200

// clear messageArea

 

201

messageArea.setText( "" );

 

202

 

 

203

// update GUI components

 

204

connectButton.setEnabled( false );

 

205

connectMenuItem.setEnabled( false );

 

206

disconnectButton.setEnabled( true );

 

207

disconnectMenuItem.setEnabled( true );

 

208

sendButton.setEnabled( true );

 

209

inputArea.setEditable( true );

 

210

inputArea.requestFocus();

 

211statusBar.setText( "Connected: " + userName );

212}

213

214 } // end ConnectListener inner class

215

216// DisconnectListener listens for user requests to disconnect

217// from DeitelMessengerServer

218private class DisconnectListener implements ActionListener {

220// disconnect from server and enable/disable GUI components

221public void actionPerformed( ActionEvent event )

222{

223 // disconnect from server and stop routing messages

224 // to messageListener

225 messageManager.disconnect( messageListener );

226

227 // update GUI componets

228 sendButton.setEnabled( false );

229 disconnectButton.setEnabled( false );

230 disconnectMenuItem.setEnabled( false );

231 inputArea.setEditable( false );

232 connectButton.setEnabled( true );

233 connectMenuItem.setEnabled( true );

234statusBar.setText( "Not Connected" );

235}

236

237 } // end DisconnectListener inner class

238

239// MyMessageListener listens for new messages from the

240// MessageManager and displays the messages in messageArea

241// using a MessageDisplayer.

242private class MyMessageListener implements MessageListener {

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 5 of 6).

Chapter 17

Networking

1051

244// when received, display new messages in messageArea

245public void messageReceived( String from, String message )

246{

247

// append message using MessageDisplayer and

248

// invokeLater, ensuring thread-safe access messageArea

249

SwingUtilities.invokeLater(

250

new MessageDisplayer( from, message ) );

251

 

252

} // end method messageReceived

253

 

254

} // end MyMessageListener inner class

255

 

256// MessageDisplayer displays a new messaage by

257// appending the message to the messageArea JTextArea. This

258// Runnable object should be executed only on the Event

259// thread, because it modifies a live Swing component.

260private class MessageDisplayer implements Runnable {

261

262private String fromUser;

263private String messageBody;

265// MessageDisplayer constructor

266public MessageDisplayer( String from, String body )

267{

268 fromUser = from;

269messageBody = body;

270}

271

272// display new message in messageArea

273public void run()

274{

275

// append new

message

276

messageArea.append( "\n" + fromUser + "> " +

277

messageBody );

278

 

 

279

// move caret

to end of messageArea to ensure new

280

// message is

visible on screen

281

messageArea.setCaretPosition(

282messageArea.getText().length() );

283}

284

285} // end MessageDisplayer inner class

286}

Fig. 17.20 ClientGUI subclass of JFrame for presenting a GUI for viewing and sending chat messages (part 6 of 6).

ClientGUI uses a MessageManager (line 41) to handle all communication with the chat server. Recall that MessageManager is an interface and therefore allows ClientGUI to use any MessageManager implementation without the need to change any code in ClientGUI. Class ClientGUI also uses a MessageListener (line 44) to receive incoming messages from the MessageManager.

The ClientGUI constructor (lines 47–183) takes as an argument the MessageManager for communicating with DeitelMessengerServer. Line 52 sets the Cli-

1052 Networking Chapter 17

entGUI’s MessageManager. Line 55 creates an instance of MyMessageListener, which implements interface MessageListener. Lines 58–62 create a Server menu that contains JMenuItems for connecting to and disconnecting from the chat server. Lines 65–66 create an ImageIcon for connectButton and connectMenuItem.

Line 66 invokes method getClass (inherited from class Object) to retrieve the Class object that represents the ClientGUI class definition. Line 66 then invokes method getResource of class Class to load the connect image. The Java virtual machine loads class definitions into memory, using a class loader. Method getResource of Class uses the Class object’s class loader to specify the location of a resource, such as an image file. Specifying resource locations in this manner enables programs to avoid hard-coded or absolute paths, which can make programs more difficult to deploy. Using the techniques described here enables an applet or application to load files from locations that are relative to the location of the .class file for a given class.

Lines 69–70 create connectButton and connectMenuItem, each with the label

"Connect" and the Icon connectIcon. Line 71 invokes method setMnemonic of class JMenuItem to set the mnemonic character for keyboard access to connectMenuItem. Line 74 creates an instance of private inner class ConnectListener, which implements interface ActionListener to handle ActionEvents from connectButton and connectMenuItem. Lines 75–76 add connectListener as an ActionListener for connectButton and connectMenuItem.

Lines 79–80 create an ImageIcon for the disconnectButton and disconnectMenuItem components. Lines 83–86 create disconnectButton and disconnectMenuItem, each with the label "Disconnect" and the Icon disconnectIcon. Line 87 invokes method setMnemonic of class JMenuItem to enable keyboard access to disconnectMenuItem. Lines 90–91 invoke method setEnabled of class JButton and class JMenuItem with a false argument to disable disconnectButton and disconnectMenuItem. This prevents the user from attempting to disconnect from the server because the client is not yet connected. Lines 94–95 create an instance of private inner class DisconnectListener, which implements interface

ActionListener to handle ActionEvents from disconnectButton and disconnectMenuItem. Lines 96–97 add disconnectListener as an ActionListener for the disconnectButton and disconnectMenuItem components.

Lines 100–101 add connectMenuItem and disconnectMenuItem to the

Server JMenu. Lines 104–106 create a JPanel and add connectButton and disconnectButton to that JPanel. Line 109 creates the JTextArea messageArea, in which the client displays incoming messages. Line 112 invokes method setEnabled with a false argument, to disable editing ot the text in messageArea. Lines 113–114 invoke methods setWrapStyleWord and setLineWrap of class JTextArea to enable word wrapping in messageArea. If a message is longer than the width of the messageArea, the messageArea will wrap the text after the last word that fits on each line, making longer messages easier to read. Lines 117–120 create a JPanel for the messageArea and add the messageArea to the JPanel in a JScrollPane. The

JScrollPane adds scroll bars to the messageArea to enable the user to scroll through messages that exceed the size of messageArea.

Line 123 creates the inputArea JTextArea for entering new messages. The arguments to the JTextArea constructor specify a four-line JTextArea that is twenty char-

Chapter 17

Networking

1053

acters wide. Lines 124–125 enable word and line wrapping, and line 126 disables editing the inputArea. When the client connects to the chat server, ConnectListener enables the inputArea to allow the user to type new messages.

Line 129 creates an ImageIcon for sendButton. Line 133 creates sendButton, which the user can click to send a message the user has typed. Line 134 disables sendButton; the ConnectListener enables the sendButton when the client connects to the chat server. Lines 137–150 add an ActionListener to sendButton. Lines 143–144 invoke method sendMessage of interface MessageManager with the userName and inputArea text as arguments. This statement sends the user’s name and whatever text the user entered in inputArea to DeitelMessengerServer as a new chat message. Line 147 invokes method setText of class JTextArea with an empty String argument to clear the inputArea for the next message.

Lines 154–157 use a BoxLayout to arrange the inputArea and sendButton. Line 155 places inputArea in a JScrollPane to enable scrolling of long messages. Line 157 adds the Box containing inputArea and sendButton to the SOUTH region of messagePanel. Lines 160–162 create the statusBar JLabel. This JLabel displays whether the client is connected to or disconnected from the chat server. Lines 161– 162 invoke method setBorder of class JLabel and create a new BevelBorder of type BevelBorder.LOWERED. This border makes the JLabel appear recessed, as is common with status bars in many applications. Lines 165–168 lay out buttonPanel, messagePanel and statusBar in the ClientGUI JFrame.

Lines 171–181 add a WindowListener to the ClientGUI JFrame. Line 177 invokes method disconnect of interface MessageManager to disconnect from the chat server in case the user quits while still connected.

Inner class ConnectListener (lines 187–214) handles events from connectButton and connectMenuItem. Line 194 invokes method connect of class MessageManager to connect to the chat server. Line 194 passes as an argument to method connect the MessageListener to which new messages should be delivered. Lines 197-198 prompt the user for a user name, and line 201 clears the messageArea JTextArea. Lines 204–209 enable the components for disconnecting from the server and for sending messages and disable components for connecting to the server. Line 210 invokes method requestFocus of class JTextArea to place the text-input cursor in the inputArea so the user can begin typing a message more easily.

Inner class DisconnectListener (lines 218–237) handles events from disconnectButton and disconnectMenuItem. Line 225 invokes method disconnect of class MessageManager to disconnect from the chat server. Lines 228–234 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 242–254) implements interface MessageListener to receive incoming messages from the MessageManager. When a new message is received, the program invokes method messageReceived (lines 242– 252) with the user name of the sender and the message body. Lines 249–250 invoke static method invokeLater of class SwingUtilties with a new instance of MessageDisplayer to append the new message to messageArea. Recall, from Chapter 15, that Swing components should be accessed only from the event dispatching thread. Method messageReceived is invoked by the PacketReceivingThread in

1054

Networking

Chapter 17

class SocketMessageManager and therefore cannot append the message text to messageArea directly, as this would occur in PacketReceivingThread, not the eventdispatch thread.

Inner class MessageDisplayer (lines 260–285) implements interface Runnable to provide a thread-safe way to append text to the messageArea JTextArea. The MessageDisplayer constructor (lines 266–270) takes as arguments the user name and message to send. Method run (lines 273–283) appends the user name, "> " and messageBody to messageArea. Lines 281–282 invoke method setCaretPosition of class JTextArea to scroll messageArea to the bottom to display the most recently received message. Instances of class MessageDisplayer should execute only as part of the event-dispatching thread, to ensure thread-safe access to the messageArea Swing component.

Class DeitelMessenger (Fig. 17.21) launches the client for the DeitelMessengerServer. Lines 18–21 create a new SocketMessageManager to connect to the DeitelMessengerServer with the IP address specified as a command-line argument to the application. Lines 24–27 create a ClientGUI for the MessageManager, set the ClientGUI size and make the ClientGUI visible.

1// DeitelMessenger.java

2 // DeitelMessenger is a chat application that uses a ClientGUI 3 // and SocketMessageManager to communicate with

4// DeitelMessengerServer.

5 package com.deitel.messenger.sockets.client;

6

7// Deitel packages

8 import com.deitel.messenger.*;

9

10 public class DeitelMessenger {

11

12// execute application

13public static void main( String args[] )

14{

15MessageManager messageManager;

16

17// create new DeitelMessenger

18if ( args.length == 0 )

19messageManager = new SocketMessageManager( "localhost" );

20else

21

messageManager = new SocketMessageManager( args[ 0 ] );

22

 

23// create GUI for SocketMessageManager

24ClientGUI clientGUI = new ClientGUI( messageManager );

25clientGUI.setSize( 300, 400 );

26clientGUI.setResizable( false );

27clientGUI.setVisible( true );

28}

29}

Fig. 17.21 DeitelMessenger application for participating in a DeitelMessengerServer chat session (part 1 of 3).

Chapter 17

Networking

1055

 

 

 

 

 

 

Fig. 17.21 DeitelMessenger application for participating in a DeitelMessengerServer chat session (part 2 of 3).

1056

Networking

Chapter 17

 

 

 

 

 

 

Fig. 17.21 DeitelMessenger application for participating in a DeitelMessengerServer chat session (part 3 of 3).

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.

17.11 (Optional) Discovering Design Patterns: Design Patterns Used in Packages java.io and java.net

This section introduces those design patterns associated with the Java file, streams and networking packages.

17.11.1 Creational Design Patterns

We now continue our discussion of creational design patterns.

Abstract Factory

Like the Factory Method design pattern, the Abstract Factory design pattern allows a system to determine the subclass from which to instantiate an object at run time. Often, this subclass is unknown during development. However, Abstract Factory uses an object known as a factory that uses an interface to instantiate objects. A factory creates a product; in this case, that product is an object of a subclass determined at run time.

The Java socket library in package java.net uses the Abstract Factory design pattern. A socket describes a connection, or a stream of data, between two computers. Class Socket references an object of a SocketImpl subclass (Section 17.5). Class Socket also contains a static reference to an object implementing interface SocketImplFactory. The Socket constructor invokes method createSocketImpl of interface SocketFactory to create the SocketImpl object. The object that imple-

Chapter 17

Networking

1057

ments interface SocketFactory is the factory, and an object of a SocketImpl subclass is the product of that factory. The system cannot specify the SocketImpl subclass from which to instantiate until run time, because the system has no knowledge of what type of Socket implementation is required (e.g., a socket configured to the local network’s security requirements). Method createSocketImpl decides the SocketImpl subclass from which to instantiate the object at run time.

17.11.2 Structural Design Patterns

This section concludes our discussion of structural design patterns.

Decorator

Let us reexamine class CreateSequentialFile (Fig. 16.6). Lines 127–128 of this class allow an ObjectOutputStream object, which writes objects to a file, to gain the responsibilities of a FileOutputStream object, which provides methods for writing bytes to files. Class CreateSequentialFile appears to “chain” objects—a FileOutputStream object is the argument to the ObjectOutputStream’s constructor. The fact that the ObjectOutputStream object can gain the behavior of a FileOutputStream dynamically prevents the need for creating a separate class called ObjectFileOutputStream, which would implement the behaviors of both classes.

Lines 127–128 of class CreateSequentialFile show an example of the Decorator design pattern, which allows an object to gain additional responsibilities dynamically. Using this pattern, designers do not have to create separate, unnecessary classes to add responsibilities to objects of a given class.

Let us consider a more complex example to discover how the Decorator design pattern can simplify a system’s structure. Suppose we wanted to enhance the I/O-performance of the previous example by using a BufferedOutputStream. Using the Decorator design pattern, we would write

output = new ObjectOutputStream( new BufferedOutputStream(

new FileOutputStream( fileName ) ) );

We can chain objects in this manner, because ObjectOutputStream, BufferedOutputStream and FileOutputStream extend abstract superclass OutputStream, and each subclass constructor takes an OutputStream object as a parameter. If the stream objects in package java.io did not use the Decorator pattern (i.e., did not satisfy these two requirements), package java.io would have to provide classes BufferedFileOutputStream, ObjectBufferedOutputStream, ObjectBufferedFileOutputStream and ObjectFileOutputStream. Consider how many classes we would have to create if we chained even more stream objects without applying the Decorator pattern.

Facade

When driving a car, you know that pressing the gas pedal accelerates your car, but you are unaware of exactly how the gas pedal causes your car to accelerate. This principle is the foundation of the Facade design pattern, which allows an object—called a facade object— to provide a simple interface for the behaviors of a subsystem—an aggregate of objects that comprise collectively a major system responsibility. The gas pedal, for example, is the facade object for the car’s acceleration subsystem, the steering wheel is the facade object for

1058

Networking

Chapter 17

the car’s steering subsystem and the brake is the facade object for the car’s deceleration subsystem. A client object uses the facade object to access the objects behind the facade. The client remains unaware of how the objects behind the facade fulfill responsibilities, so the subsystem complexity is hidden from the client. When you press the gas pedal you act as a client object. The Facade design pattern reduces system complexity, because a client interacts with only one object (the facade). This pattern shields applications developers from subsystem complexities. Developers need to be familiar with only the operations of the facade object, rather than with the more detailed operations of the entire subsystem.

In package java.net, an object of class URL is a facade object. This object contains a reference to an InetAddress object that specifies the host computer’s IP address. The URL facade object also references an object from class URLStreamHandler, which opens the URL connection. The client object that uses the URL facade object accesses the InetAddress object and the URLStreamHandler object through the facade object. However, the client object does not know how the objects behind the URL facade object accomplish their responsibilities.

17.11.3 Architectural Patterns

Design patterns allow developers to design specific parts of systems, such as abstracting object instantiations or aggregating classes into larger structures. Design patterns also promote loose coupling among objects. Architectural patterns promote loose coupling among subsystems. These patterns specify all subsystems in the system and how they interact with each other.1 We introduce the popular Model-View-Controller and Layers architectural patterns.

MVC

Consider the design of a simple text editor. In this program, the user inputs text from the keyboard and formats this text using the mouse. Our program stores this text and format information into a series of data structures, then displays this information on screen for the user to read what has been inputted.

This program adheres to the Model-View-Controller (MVC) architectural pattern, which separates application data (contained in the model) from graphical presentation components (the view) and input-processing logic (the controller).2 Figure 17.22 shows the relationships between components in MVC.

 

modifies

notifies

Controller

Model

View

Fig. 17.22 Model-View-Controller Architecture.

1.R. Hartman. “Building on Patterns.” Application Development Trends May 2001: 19–26.

2.Section 13.17 also discussed Model-View-Controller architecture and its relevance to the elevator simulation case study.

Chapter 17

Networking

1059

The controller implements logic for processing user inputs. The model contains application data, and the view presents the data stored in the model. When a user provides some input, the controller modifies the model with the given input. The model contains the application data. With regards to the text-editor example, the model might contain only the characters that make up the document. When the model changes, it notifies the view of the change so the view can update its presentation with the changed data. The view in a word processor might display characters using a particular font, with a particular size, etc.

MVC does not restrict an application to a single view and a single controller. In a more sophisticated program (e.g., a word processor), there might be two views of a document model. One view might display an outline of the document and the other might display the complete document. The word processor also might implement multiple controllers—one for handling keyboard input and another for handling mouse selections. If either controller makes a change in the model, both the outline view and the print-preview window will show the change immediately when the model notifies all views of changes.

Another key benefit to the MVC architectural pattern is that developers can modify each component individually without having to modify the other components. For example, developers could modify the view that displays the document outline, but the developers would not have to modify either the model or other views or controllers.

Layers

Consider the design in Fig. 17.23, which presents the basic structure of a three-tier application, in which each tier contains a unique system component.

Client tier (Top tier)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Middle tier

 

Application

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Information tier (Bottom tier)

Database

Fig. 17.23 Three-tier application model.

1060

Networking

Chapter 17

The information tier (also called the “bottom tier”) maintains data for the application, typically storing the data in a database. The information tier for an online store may contain product information, such as descriptions, prices and quantities in stock and customer information, such as user names, billing addresses and credit-card numbers.

The middle tier acts as an intermediary between the information tier and the client tier. The middle tier processes client-tier requests, reads data from and writes data to the database. The middle tier then processes data from the information tier and presents the content to the client tier. This processing is the application’s business logic, which handles such tasks as retrieving data from the information tier, ensuring that data is reliable before updating the database and presenting data to the client tier. For example, the business logic associated with the middle tier for the online store can verify a customer’s credit card with the credit-card issuer before the warehouse ships the customer’s order. This business logic could then store (or retrieve) the credit information in the database and notify the client tier that the verification was successful.

The client tier (also called the “top tier”) is the application’s user interface, such as a standard Web browser. Users interact directly with the application through the user interface. The client tier interacts with the middle tier to make requests and retrieve data from the information tier. The client tier then displays data retrieved from the middle tier.

Figure 17.23 is an implementation of the Layers architectural pattern, which divides functionality into separate layers. Each layer contains a set of system responsibilities and depends on the services of only the next lower layer. In Fig. 17.23, each tier corresponds to a layer. This architectural pattern is useful, because a designer can modify one layer without having to modify the other layers. For example, a designer could modify the information tier in Fig. 17.23 to accommodate a particular database product, but the designer would not have to modify either the client tier or the middle tier.

17.11.4 Conclusion

In this “Discovering Design Patterns” section, we discussed how packages java.io and java.net take advantage of specific design patterns and how developers can integrate design patterns with networking/file applications in Java. We also introduced the Model- View-Controller and Layers architectural patterns, which both assign system functionality to separate subsystems. These patterns make designing a system easier for developers. In “Discovering Design Patterns” Section 21.12, we conclude our presentation of design patterns by discussing those design patterns used in package java.util.

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 continuous 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. This is not the right protocol for everyday users, because, unlike TCP, the protocol used, UDP—the User Datagram Pro- tocol—is a connectionless service and does not guarantee that packets arrive in any particular way. In fact, packets can be lost, can be duplicated and can even arrive out of sequence. So, with UDP, significant extra programming is required on the user’s part to deal with these problems (if the user chooses to do so).

Chapter 17

Networking

1061

The HTTP protocol (Hypertext Transfer Protocol) that forms the basis of the World Wide Web uses URIs (Uniform Resource Identifiers, also called URLs or Uniform Resource Locators) to locate data on the Internet. Common URIs represent files or directories and can represent complex tasks such as database lookups and Internet searches.

Web browsers often restrict an applet so that it can communicate only with the machine from which it was originally downloaded.

A Hashtable stores key/value pairs. A program uses a key to store and retrieve an associated value in the Hashtable. Hashtable method put takes two arguments—a key and its associated value—and places the value in the Hashtable at a location determined by the key. Hashtable method get takes one argument—a key—and retrieves the value (as an Object reference) associated with the key.

A Vector is a dynamically resizable array of Objects. Vector method addElement adds a new element to the end of the Vector.

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 object as an argument and passes it to the AppletContext (i.e., the browser), which displays the World Wide Web resource associated with that URL.

A second version of AppletContext method showDocument enables an applet to specify the target frame in which Web resource should be displayed. Special target frames include _blank (display the content from the specified URI in a new Web browser window), _self (display the content from the specified URI in the same frame as the applet) and _top (the browser should remove the current frames, then display the content from the specified URI in the current window).

Component method setCursor changes the mouse cursor when the cursor is positioned over a specific GUI component. The Cursor constructor receives an integer indicating the cursor type (such as Cursor.WAIT_CURSOR or Cursor.DEFAULT_CURSOR).

JEditorPane method setPage downloads the document specified by its argument and displays it in the JEditorPane.

Typically, an HTML document contains hyperlinks—text, 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 (package javax.swing.event) and notifies all registered HyperlinkListeners (package javax.swing.event) of that event.

HyperlinkEvent method getEventType determines the type of the HyperlinkEvent. Class HyperlinkEvent contains public static inner class EventType, which defines three hyperlink event types: ACTIVATED (user clicked a hyperlink), ENTERED (user moved the mouse over a hyperlink) and EXITED (user moved the mouse 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 specifies the number of clients that can wait for a connection and be processed by 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 method getOutputStream gets a reference to the OutputStream associated with a Socket. Socket method getInputStream gets a reference to the InputStream associated with the Socket.

1062

Networking

Chapter 17

When transmission over a Socket connection is complete, the server closes the connection by invoking the Socket’s close method.

A Socket object connects a client to a server by specifying the server name and port number when creating the Socket object. A failed connection attempt throws an IOException.

When the InputStream method read returns –1, the stream detects that the end-of-stream has been reached.

An EOFException occurs when a ObjectInputStream attempts to read a value from a stream on which end-of-stream is detected.

InetAddress method getByName returns an InetAddress object containing the host name of the computer for which the String host name or String Internet address is specified as an argument.

InetAddress method getLocalHost returns an InetAddress object containing the host name of the local computer executing the program.

The port at which a client connects to a server is sometimes called the handshake point.

Connection-oriented transmission is like the telephone system—you 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. Each of the letters is then mailed at once. The letters could arrive in order, out of order or not at all.

DatagramPacket objects store packets of data for sending or store packets of data 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.

DatagramPacket method getPort returns an integer specifying the port number through which the host computer sent the DatagramPacket.

DatagramPacket method getLength returns an integer representing the number of bytes of data in a DatagramPacket.

DatagramPacket method getData returns a byte array containing the data in a DatagramPacket.

The DatagramPacket constructor for a packet to be sent takes four arguments—the 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 call—the current thread is put in the blocked state while the thread waits for the read operation to complete. Method setSoTimeout specifies that, if no data is received in the given number of milliseconds, the Socket should issue an Inter-

Chapter 17

Networking

1063

ruptedIOException, which the current thread can catch, then continue executing. This prevents the current thread from deadlocking if there is no more data available from the Socket.

Multicast is an efficient way to send data to many clients without the overhead of broadcasting that data to every host on the Internet.

Using multicast, an application can “publish” DatagramPackets to be delivered to other appli- cations—the “subscribers.”

An application multicasts DatagramPackets by sending the DatagramPackets to a multicast address—an 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 connect to the multicast address to join the multicast group that will receive the published DatagramPackets.

Multicast DatagramPackets are not reliable—packets are not guaranteed to reach any destination. Also, the order in which clients receive the datagrams is not guaranteed.

The MulticastSocket constructor takes as an argument the port to which the MulticastSocket should connect 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.

TERMINOLOGY

accept a connection

accept method of ServerSocket class addElement method of class Vector AppletContext interface

bind to a port

BindException class client

client connects to a server client/server relationship client-side socket

close a connection

close method of class Socket collaborative computing computer networking

connect to a port

connect to a World Wide Web site

ConnectException class connection

connection request connectionless service

connectionless transmission with datagrams connection-oriented service

Cursor class

Cursor.DEFAULT_CURSOR Cursor.WAIT_CURSOR datagram

datagram socket

DatagramPacket class DatagramSocket class

deny a connection duplicated packets

get method of class Hashtable getAddress method of DatagramPacket getAppletContext method of class Applet getByName method of InetAddress getData method of class DatagramPacket getEventType method

getInputStream method of class Socket getLength method of DatagramPacket getLocalHost method

getLocalHost method of InetAddress getOutputStream method of class Socket getPort method of class DatagramPacket getPredefinedCursor method of Cursor getURL method of class HyperlinkEvent handshake point

Hashtable class heterogeneous computer systems host

Hyperlink.EventType class

Hyperlink.EventType.ACTIVATED

Hyperlink.EventType.ENTERED

Hyperlink.EventType.EXITED HyperlinkEvent class HyperlinkListener interface hyperlinkUpdate method InetAddress class

Internet

1064

Networking

Chapter 17

Internet address

register an available port number

InterruptedIOException class

send method of class DatagramSocket

IOException class

server

Java Security API

server-side socket

java.net package

ServerSocket class

JEditorPane class

setCursor method of class Component

joinGroup method of MulticastSocket

setPage method of class JEditorPane

key/value pair

setSoTimeout method of class Socket

leaveGroup method of MulticastSocket

showDocument method of AppletContext

lost packets

socket

MalformedURLException class multicast

multicast address multicast group

MulticastSocket class multithreaded server network programming networking

open a socket out-of-sequence packets packet

packet length port

port number on a server

put method of class Hashtable read from a socket

receive method of class DatagramSocket

Socket class

socket-based communications

SocketException stream socket

TCP (Transmission Control Protocol) UDP (User Datagram Protocol)

UnknownHostException

URI (Uniform Resource Identifier) URL (Uniform Resource Locator) URL class

Vector class

wait for a connection wait for a packet Web browser

Web server World Wide Web

SELF-REVIEW EXERCISES

17.1Fill in the blanks in each of the following statements:

a)

Exception

 

 

 

 

occurs when an input/output error occurs when closing a socket.

b)

Exception

 

 

 

 

occurs when a server address indicated by a client cannot be re-

 

solved.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

c)

If a DatagramSocket constructor fails to set up a DatagramSocket properly, an

 

exception of type

 

 

 

 

 

 

 

 

occurs.

 

 

 

 

 

d)

Many of Java’s networking classes are contained in package

 

 

.

 

 

e)

Class

 

 

binds the application to a port for datagram transmission.

f)

An object of class

 

 

 

 

 

 

 

 

contains an Internet address.

 

 

 

 

 

g)

The two types of sockets we discussed in this chapter are

 

 

 

sockets and

 

 

 

 

 

 

sockets.

 

 

 

 

 

 

 

 

 

 

 

 

 

h)

The acronym URL stands for

 

 

.

 

 

 

 

 

 

 

 

i)

The acronym URI stands for

 

 

.

 

 

 

 

 

 

 

 

 

j)

The key protocol that forms the basis of the World Wide Web is

 

.

k)

AppletContext method

 

 

 

receives a URL object as an argument and dis-

 

plays in a browser the World Wide Web resource associated with that URL.

l)

InetAddress method getLocalHost returns an

 

 

object containing the

 

local host name of the computer on which the program is executing.

m)

Method

 

 

 

of class MulticastSocket subscribes the MulticastSock-

 

et to a multicast group.

 

 

 

 

 

 

 

 

 

 

 

 

 

Chapter 17

Networking

1065

n)The URL constructor determines whether the String passed as an argument represents a valid Uniform Resource Identifier. If so, the URL object is initialized to contain the

URI; otherwise, an exception of type

 

occurs.

17.2State whether each of the following is true or false. If false, explain why.

a)An application that uses multicast broadcasts DatagramPackets to every host on the Internet.

b)UDP is a connection-oriented protocol.

c)With stream sockets a process establishes a connection to another process.

d)A server waits at a port for connections from a client.

e)Datagram packet transmission over a network is reliable—packets are guaranteed to arrive in sequence.

f)For security reasons, many Web browsers such as Netscape Communicator allow Java applets to do file processing only on the machines on which they execute.

g)Web browsers often restrict an applet so that it can only communicate with the machine from which it was originally downloaded.

h)IP addresses in the range from 224.0.0.0 to 239.255.255.255 are reserved for multicast.

ANSWERS TO SELF-REVIEW EXERCISES

17.1 a) IOException. b) UnknownHostException. c) SocketException. d) java.net. e) DatagramSocket. f) InetAddress. g) stream, datagram. h) Uniform Resource Locator. i) Universal Resource Identifier. j) http. k) showDocument. l) InetAddress. m) joinGroup. n) MalformedURLException.

17.2a) False; multicast sends DatagramPackets only to hosts that have joined the multicast

group. b) False; UDP is a connectionless protocol and TCP is a connection-oriented protocol. c) True. d) True. e) False; packets could be lost and packets can arrive out of order. f) False; most browsers prevent applets from doing file processing on the client machine. g) True. h) True.

EXERCISES

17.3Distinguish between connection-oriented and connectionless network services.

17.4How does a client determine the host name of the client computer?

17.5Under what circumstances would a SocketException be thrown?

17.6How can a client get a line of text from a server?

17.7Describe how a client connects to a server.

17.8Describe how a server sends data to a client.

17.9Describe how to prepare a server to receive a stream-based connection request from a single

client.

17.10Describe how to prepare a server to receive connection requests from multiple clients if each client that connects should be processed in parallel with all other connected clients.

17.11How does a server listen for connections at a port?

17.12What determines how many connect requests from clients can wait in a queue to connect to a server?

17.13As described in the text, what reasons might cause a server to refuse a connection request from a client?

1066

Networking

Chapter 17

17.14Use a socket connection to allow a client to specify a file name and have the server send the contents of the file or indicate that the file does not exist.

17.15Modify Exercise 17.14 to allow the client to modify the contents of the file and send the file back to the server for storage. The user can edit the file in a JTextArea, then click a save changes button to send the file back to the server.

17.16Modify program of Fig. 17.2 to allow users to add their own sites to the list and remove sites from the list.

17.17Multithreaded servers are quite popular today, especially because of the increasing use of multiprocessing servers. Modify the simple server application presented in Section 17.6 to be a multithreaded server. Then use several client applications and have each of them connect to the server simultaneously. Use a Vector to store the client threads. Vector provides several methods of use in this exercise. Method size determines the number of elements in a Vector. Method elementAt returns the element in the specified location (as an Object reference). Method add places a new element at the end of the Vector. Method remove deletes its argument from the Vector. Method lastElement returns an Object reference to the last object you inserted in the Vector.

17.18In the text, we presented a tic-tac-toe program controlled by a multithreaded server. Develop a checkers program modeled after the tic-tac-toe program. The two users should alternate making moves. Your program should mediate the players’ moves, determining whose turn it is and allowing only valid moves. The players themselves will determine when the game is over.

17.19Develop a chess-playing program modeled after the checkers program in the Exercise 17.18.

17.20Develop a Blackjack card game program in which the server application deals cards to each of the client applets. The server should deal additional cards (as per the rules of the game) to each player as requested.

17.21Develop a Poker card game in which the server application deals cards to each of the client applets. The server should deal additional cards (as per the rules of the game) to each player as requested.

17.22(Modifications to the Multithreaded Tic-Tac-Toe Program) The programs of Fig. 17.8 and Fig. 17.9 implemented a multithreaded, client/server version of the game Tic-Tac-Toe. Our goal in developing this game was to demonstrate a multithreaded server that could process multiple connections from clients at the same time. The server in the example is really a mediator between the two client applets—it makes sure that each move is valid and that each client moves in the proper order. The server does not determine who won or lost or if there was a draw. Also, there is no capability to allow a new game to be played or to terminate an existing game.

The following is a list of suggested modifications to the multithreaded Tic-Tac-Toe application and applet.

a)Modify the TicTacToeServer class to test for a win, loss or draw on each move in the game. Send a message to each client applet that indicates the result of the game when the game is over.

b)Modify the TicTacToeClient class to display a button that when clicked allows the client to play another game. The button should be enabled only when a game completes. Note that both class TicTacToeClient and class TicTacToeServer must be modified to reset the board and all state information. Also, the other TicTacToeClient should be notified that a new game is about to begin so its board and state can be reset.

c)Modify the TicTacToeClient class to provide a button that allows a client to terminate the program at any time. When the user clicks the button, the server and the other client should be notified. The server should then wait for a connection from another client so a new game can begin.

d)Modify the TicTacToeClient class and the TicTacToeServer class so the winner of a game can choose game piece X or O for the next game. Remember: X always goes first.

Chapter 17

Networking

1067

e)If you would like to be ambitious, allow a client to play against the server while the server waits for a connection from another client.

17.23(3-D Multithreaded Tic-Tac-Toe) Modify the multithreaded, client/server Tic-Tac-Toe program to implement a three-dimensional 4-by-4-by-4 version of the game. Implement the server application to mediate between the two clients. Display the three-dimensional board as four boards containing four rows and four columns each. If you would like to be ambitious, try the following modifications:

a)Draw the board in a three-dimensional manner.

b)Allow the server to test for a win, loss or draw. Beware! There are many possible ways to win on a 4-by-4-by-4 board!

17.24(Networked Morse Code) Modify your solution to Exercise 10.27 to enable two applets to send Morse Code messages to each other through a multithreaded server application. Each applet should allow the user to type normal characters in JTextAreas, translate the characters into Morse Code and send the coded message through the server to the other client. When messages are received, they should be decoded and displayed as normal characters and as Morse Code. The applet should have two JTextAreas: one for displaying the other client’s messages and one for typing.

18

Multimedia:

Images, Animation,

Audio and Video

Objectives

To understand how to get and display images.

To create animations from sequences of images.

To customize an animation applet with applet parameters specified in the applet’s HTML document.

To create image maps.

To be able to get, play, loop and stop sounds using an

AudioClip.

The wheel that squeaks the loudest … gets the grease.

John Billings (Henry Wheeler Shaw)

We’ll use a signal I have tried and found far-reaching and easy to yell. Waa-hoo!

Zane Grey

There is a natural hootchy-kootchy motion to a goldfish.

Walt Disney

Between the motion and the act falls the shadow.

Thomas Stearns Eliot, The Hollow Men

Chapter 18

Multimedia: Images, Animation, Audio and Video

1069

Outline

18.1Introduction

18.2Loading, Displaying and Scaling Images

18.3Animating a Series of Images

18.4Customizing LogoAnimator via Applet Parameters

18.5Image Maps

18.6Loading and Playing Audio Clips

18.7Internet and World Wide Web Resources

Summary • Terminology • Self-Review Exercises • Answers to Self-Review Exercises • Exercises

18.1 Introduction

Welcome to what may be the largest revolution in the history of the computer industry. Those of us who entered the field decades ago were interested in using computers primarily to perform arithmetic calculations at high speed. As the computer field evolves, we are beginning to realize that the data-manipulation capabilities of computers are now equally important. The “sizzle” of Java is multimedia, the use of sound, images, graphics and video to make applications “come alive.” Today, many people consider two-dimensional color video to be the “ultimate” in multimedia. Within the decade, we expect all kinds of exciting new three-dimensional applications. Java programmers already can use the Java3D API to create substantial 3D graphics applications. We discuss the Java3D API in our book Advanced Java 2 Platform How to Program.

Multimedia programming offers many new challenges. The field is already enormous and will grow rapidly. People are rushing to equip their computers for multimedia. Most new computers sold today are “multimedia ready,” with CD or DVD drives, audio boards and sometimes with special video capabilities.

Among users who want graphics, two-dimensional graphics no longer suffice. Many people now want three-dimensional, high-resolution, color graphics. True three-dimensional imaging may become available within the next decade. Imagine having ultra-high-resolution, “theater-in-the-round,” three-dimensional television. Sporting and entertainment events will seem to take place on your living room floor! Medical students worldwide will see operations being performed thousands of miles away, as if they were occurring in the same room. People will be able to learn how to drive with extremely realistic driving simulators in their homes before they get behind the wheel. The possibilities are exciting and endless.

Multimedia demands extraordinary computing power. Until recently, affordable computers with this kind of power were not available. Today’s ultrafast processors, like the SPARC Ultra from Sun Microsystems, the Pentium and Itanium from Intel, the Alpha from Compaq Computer Corporation and the processors from MIPS/Silicon Graphics (among others) make effective multimedia possible. The computer and communications industries will be primary beneficiaries of the multimedia revolution. Users will be willing to pay for the faster processors, larger memories and wider communications bandwidths that support demanding multimedia applications. Ironically, users may not have to pay more as fierce competition in these industries drives prices down.