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

990

Networking

Chapter 17

59). Lines 56–57 use HyperlinkEvent method getEventType to determine the type of the HyperlinkEvent. Class HyperlinkEvent contains public inner class

EventType that defines three hyperlink event types: ACTIVATED (the user clicked a hyperlink to change Web pages), ENTERED (the user moved the mouse over a hyperlink) and EXITED (the user moved the mouse away from a hyperlink). If a hyperlink was ACTIVATED, line 58 uses HyperlinkEvent method getURL to obtain the URL represented by the hyperlink. Method toString converts the returned URL to a String format that can be passed to utility method getThePage.

Software Engineering Observation 17.1

A JEditorPane generates HyperlinkEvents only if it is uneditable.

17.4 Establishing a Simple Server Using Stream Sockets

The two examples discussed so far use high-level Java networking capabilities to communicate between applications. In those examples, it was not the Java programmer’s responsibility to establish the connection between a client and a server. The first program relied on the Web browser to communicate with a Web server. The second program relied on a JEditorPane to perform the connection. This section begins our discussion of creating your own applications that can communicate with one another.

Establishing a simple server in Java requires five steps. Step 1 is to create a ServerSocket object. A call to the ServerSocket constructor such as

ServerSocket server = new ServerSocket( port, queueLength );

registers an available port number and specifies a maximum number of clients that can wait to connect to the server (i.e., the queueLength). The port number is used by clients to located the server application on the server computer. This often is called the handshake point. If the queue is full, the server refuses client connections. The preceding statement establishes the port where the server waits for connections from clients (a process known as binding the server to the port). Each client will ask to connect to the server on this port.

Programs manage each client connection with a Socket object. After binding the server to a port with a ServerSocket (Step 2), the server listens indefinitely (or blocks) for an attempt by a client to connect. To listen for a client, the program calls ServerSocket method accept, as in

Socket connection = server.accept();

This statement returns a Socket object when a connection with a client is established. Step 3 is to get the OutputStream and InputStream objects that enable the

server to communicate with the client by sending and receiving bytes. The server sends information to the client via an OutputStream object. The server receives information from the client via an InputStream object. To obtain the streams, the server invokes method getOutputStream on the Socket to get a reference to the OutputStream associated with the Socket and invokes method getInputStream on the Socket to get a reference to the InputStream associated with the Socket.

The OutputStream and InputStream objects can be used to send or receive individual bytes or sets of bytes with the OutputStream method write and the InputStream method read, respectively. Often it is useful to send or receive values of primitive

Chapter 17

Networking

991

data types (such as int and double) or Serializable class data types (such as String) rather than sending bytes. In this case, we can use the techniques of Chapter 16 to chain other stream types (such as ObjectOutputStream and ObjectInputStream) to the OutputStream and InputStream associated with the Socket. For example,

ObjectInputStream input =

new ObjectInputStream( connection.getInputStream() );

ObjectOutputStream output =

new ObjectOutputStream( connection.getOutputStream() );

The beauty of establishing these relationships is that whatever the server writes to the ObjectOutputStream is sent via the OutputStream and is available at the client’s InputStream and whatever the client writes to its OutputStream (with a corresponding ObjectOutputStream) is available via the server’s InputStream.

Step 4 is the processing phase, in which the server and the client communicate via the InputStream and OutputStream objects. In Step 5, when the transmission is complete, the server closes the connection by invoking the close method on the Socket and on the corresponding streams.

Software Engineering Observation 17.2

With sockets, network I/O appears to Java programs to be identical to sequential file I/O. Sockets hide much of the complexity of network programming from the programmer.

Software Engineering Observation 17.3

With Java’s multithreading, we can create multithreaded servers that can manage many si- multaneous connections with many clients; this multithreaded-server architecture is precisely what popular network servers use.

Software Engineering Observation 17.4

A multithreaded server can take the Socket returned by each call to accept and create a new thread that manages network I/O across that Socket, or a multithreaded server can maintain a pool of threads (a set of already existing threads) ready to manage network I/O across the new Sockets as they are created.

Performance Tip 17.2

In high-performance systems in which memory is abundant, a multithreaded server can be implemented to create a pool of threads that can be assigned quickly to handle network I/O across each new Socket as it is created. Thus, when the server receives a connection, the server need not incur the overhead of thread creation.

17.5 Establishing a Simple Client Using Stream Sockets

Establishing a simple client in Java requires four steps. In Step 1, we create a Socket to connect to the server. The Socket constructor establishes the connection to the server. For example, the statement

Socket connection = new Socket( serverAddress, port );

uses the Socket constructor with two arguments—the server’s Internet address (serverAddress) and the port number. If the connection attempt is successful, this statement returns

992

Networking

Chapter 17

a Socket. A connection attempt that fails throws an instance of a subclass of IOException, so many programs simply catch IOException. An UnknownHostException occurs when a server address indicated by a client cannot be resolved. A ConnectException is thrown when an error occurs while attempting to connect to a server.

In Step 2, the client uses Socket methods getInputStream and getOutputStream to obtain references to the Socket’s InputStream and OutputStream. As we mentioned in the preceding section, often it is useful to send or receive values of primitive data types (such as int and double) or class data types (such as String and Employee) rather than sending bytes. If the server is sending information in the form of actual data types, the client should receive the information in the same format. Thus, if the server sends values with an ObjectOutputStream, the client should read those values with an ObjectInputStream.

Step 3 is the processing phase in which the client and the server communicate via the InputStream and OutputStream objects. In Step 4, the client closes the connection when the transmission is complete by invoking the close method on the Socket and the corresponding streams. When processing information sent by a server, the client must determine when the server is finished sending information so the client can call close to close the Socket connection. For example, the InputStream method read returns the value –1 when it detects end-of-stream (also called EOF—end-of-file). If an ObjectInputStream is used to read information from the server, an EOFException occurs when the client attempts to read a value from a stream on which end-of-stream is detected.

17.6 Client/Server Interaction with Stream Socket Connections

The applications of Fig. 17.4 and Fig. 17.5 use stream sockets to demonstrate a simple client/server chat application. The server waits for a client connection attempt. When a client application connects to the server, the server application sends a String object (remember that Strings are Serializable) indicating that the connection was successful to the client. Then the client displays the message. Both the client and the server applications contain JTextFields, which allow the user to type a message and send it to the other application. When the client or the server sends the String TERMINATE”, the connection between the client and the server terminates. Then the server waits for the next client to connect. The definition of class Server appears in Fig. 17.4. The definition of class Client appears in Fig. 17.5. The screen captures showing the execution between the client and the server are shown as part of Fig. 17.5.

Class Server’s constructor (lines 25–58) creates the GUI of the application (a JTextField and a JTextArea). The Server object displays its output in a JTextArea. When the main method (lines 186–194) executes, it creates an instance of class Server, specifies the window’s default close operation and calls method runServer (defined at lines 61–97).

Method runServer does the work of setting up the server to receive a connection and processing the connection when it occurs. The method creates a ServerSocket called server (line 68) to wait for connections. The ServerSocket is set up to listen for a connection from a client at port 5000. The second argument to the constructor is the number of connections that can wait in a queue to connect to the server (100 in this example). If the queue is full when a client attempts to connect, the server refuses the connection.

Chapter 17

Networking

993

1// Fig. 17.4: Server.java

2 // Set up a Server that will receive a connection 3 // from a client, send a string to the client,

4 // and close the connection.

5

6 // Java core packages

7 import java.io.*;

8 import java.net.*;

9import java.awt.*;

10 import java.awt.event.*;

11

12// Java extension packages

13import javax.swing.*;

14

15public class Server extends JFrame {

16private JTextField enterField;

17private JTextArea displayArea;

18private ObjectOutputStream output;

19private ObjectInputStream input;

20private ServerSocket server;

21private Socket connection;

22private int counter = 1;

23

24// set up GUI

25public Server()

26{

27super( "Server" );

29 Container container = getContentPane();

30

31// create enterField and register listener

32enterField = new JTextField();

33enterField.setEnabled( false );

34

 

35

enterField.addActionListener(

36

 

37

new ActionListener() {

38

 

39

// send message to client

40

public void actionPerformed( ActionEvent event )

41

{

42

sendData( event.getActionCommand() );

43

}

44

 

45

} // end anonymous inner class

46

 

47

); // end call to addActionListener

48

 

49

container.add( enterField, BorderLayout.NORTH );

50

 

51// create displayArea

52displayArea = new JTextArea();

Fig. 17.4 Server portion of a client/server stream-socket connection (part 1 of 4).

994

Networking

Chapter 17

53 container.add( new JScrollPane( displayArea ),

54 BorderLayout.CENTER );

55

56setSize( 300, 150 );

57setVisible( true );

58}

59

60// set up and run server

61public void runServer()

62{

63// set up server to receive connections;

64// process connections

65try {

66

 

67

// Step 1: Create a ServerSocket.

68

server = new ServerSocket( 5000, 100 );

69

 

70

while ( true ) {

71

 

72

// Step 2: Wait for a connection.

73

waitForConnection();

74

 

75

// Step 3: Get input and output streams.

76

getStreams();

77

 

78

// Step 4: Process connection.

79

processConnection();

80

 

81

// Step 5: Close connection.

82

closeConnection();

83

 

84

++counter;

85}

86}

87

88// process EOFException when client closes connection

89catch ( EOFException eofException ) {

90System.out.println( "Client terminated connection" );

91}

92

93// process problems with I/O

94catch ( IOException ioException ) {

95ioException.printStackTrace();

96}

97}

98

99 // wait for connection to arrive, then display connection info

100private void waitForConnection() throws IOException

101{

102displayArea.setText( "Waiting for connection\n" );

104// allow server to accept a connection

105connection = server.accept();

Fig. 17.4 Server portion of a client/server stream-socket connection (part 2 of 4).

Chapter 17

Networking

995

106

107 displayArea.append( "Connection " + counter +

108 " received from: " +

109connection.getInetAddress().getHostName() );

110}

111

112// get streams to send and receive data

113private void getStreams() throws IOException

114{

115// set up output stream for objects

116output = new ObjectOutputStream(

117

connection.getOutputStream() );

118

 

119// flush output buffer to send header information

120output.flush();

121

122// set up input stream for objects

123input = new ObjectInputStream(

124

connection.getInputStream() );

125

 

126displayArea.append( "\nGot I/O streams\n" );

127}

128

129// process connection with client

130private void processConnection() throws IOException

131{

132// send connection successful message to client

133String message = "SERVER>>> Connection successful";

134output.writeObject( message );

135output.flush();

136

137// enable enterField so server user can send messages

138enterField.setEnabled( true );

139

140// process messages sent from client

141do {

142

 

143

// read message and display it

144

try {

145

message = ( String ) input.readObject();

146

displayArea.append( "\n" + message );

147

displayArea.setCaretPosition(

148

displayArea.getText().length() );

149

}

150

 

151

// catch problems reading from client

152

catch ( ClassNotFoundException classNotFoundException ) {

153

displayArea.append( "\nUnknown object type received" );

154

}

155

 

156} while ( !message.equals( "CLIENT>>> TERMINATE" ) );

157}

158

Fig. 17.4 Server portion of a client/server stream-socket connection (part 3 of 4).

996

Networking

Chapter 17

159// close streams and socket

160private void closeConnection() throws IOException

161{

162displayArea.append( "\nUser terminated connection" );

163enterField.setEnabled( false );

164output.close();

165input.close();

166connection.close();

167}

168

169// send message to client

170private void sendData( String message )

171{

172// send object to client

173try {

174

output.writeObject( "SERVER>>> " + message );

175

output.flush();

176displayArea.append( "\nSERVER>>>" + message );

177}

178

179// process problems sending object

180catch ( IOException ioException ) {

181displayArea.append( "\nError writing object" );

182}

183}

184

185// execute application

186public static void main( String args[] )

187{

188Server application = new Server();

190

application.setDefaultCloseOperation(

191

JFrame.EXIT_ON_CLOSE );

192

 

193application.runServer();

194}

195

196 } // end class Server

Fig. 17.4 Server portion of a client/server stream-socket connection (part 4 of 4).

Software Engineering Observation 17.5

Port numbers can be between 0 and 65,535. Many operating systems reserve port numbers below 1024 for system services (such as e-mail and World Wide Web servers). Generally, these ports should not be specified as connection ports in user programs. In fact, some operating systems require special access privileges to use port numbers below 1024.

Line 73 calls method waitForConnection (lines 100–110) to wait for a client connection. After the connection is established, line 76 calls method getStreams (lines 113–127) to obtain references to the InputStream and OutputStream for the connection. Line 79 calls method processConnection to send the initial connection message to the client and to process all messages received from the client. Line 82 calls method closeConnection to terminate the connection with the client.

Chapter 17

Networking

997

In method waitForConnection (lines 100–110), line 105 uses ServerSocket method accept to wait for a connection from a client and assigns the resulting Socket to connection. This method blocks until a connection is received (i.e., the thread in which accept is called stops executing until a client connects). Lines 107–109 output the host name of the computer that made the connection. Socket method getInetAddress returns an InetAddress object (package java.net) containing information about the client computer. InetAddress method getHostName returns the host name of the client computer. For example, if the Internet address of the computer is 127.0.0.1, the corresponding host name would be localhost.

Method getStreams (lines 113–127) obtains references to the InputStream and

OutputStream of the Socket and uses them to initialize an ObjectInputStream and an ObjectOutputStream, respectively. Notice the call to ObjectOutputStream method flush at line 120. This statement causes the ObjectOutputStream on the server to send a stream header to the corresponding client’s ObjectInputStream. The stream header contains information such as the version of object serialization being used to send objects. This information is required by the ObjectInputStream so it can prepare to receive those objects correctly.

Software Engineering Observation 17.6

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

Line 134 of method processConnection (lines 130–157) uses ObjectOutputStream method writeObject to send the string “SERVER>>> Connection successful” to the client. Line 135 flushes the output stream to ensure that the object is sent immediately; otherwise, the object could be held in an output buffer until more information is available to send.

Performance Tip 17.3

Output buffers typically are used to increase the efficiency of an application by sending larg- er amounts of data fewer times. The input and output components of a computer are typically much slower than the memory of the computer.

The do/while structure at lines 141–156 loops until the server receives the message “CLIENT>>> TERMINATE.” Line 145 uses ObjectInputStream method readObject to read a String from the client. Line 146 displays the message in the JTextArea. Lines 147–148 use JTextComponent method setCaretPosition to position the input cursor in the JTextArea after the last character in the JTextArea. This scrolls the JTextArea as text is appended to it.

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

When the user of the server application enters a String in the JTextField and presses the Enter key, the program calls method actionPerformed (lines 40–43), reads the String from the JTextField and calls utility method sendData (lines 170–183).

998

Networking

Chapter 17

Method sendData sends the String object to the client, flushes the output buffer and appends the same String to the JTextArea in the server window.

Notice that the Server receives a connection, processes the connection, closes the connection and waits for the next connection. A more likely scenario would be a Server that receives a connection, sets up that connection to be processed as a separate thread of execution, and waits for new connections. The separate threads that process existing connections can continue to execute while the Server concentrates on new connection requests.

Like class Server, class Client’s (Fig. 17.5) constructor creates the GUI of the application (a JTextField and a JTextArea). The Client object displays its output in a JTextArea. When the main method (line 175–188) executes, it creates an instance of class Client, specifies the window’s default close operation and calls method runClient (defined at lines 63–90). In this example, you can execute the client from any computer on the Internet and specify the Internet address or host name of the server computer as a command-line argument to the program. For example,

java Client 192.168.1.15

connects to the Server on the computer with Internet address 192.168.1.15.

1// Fig. 17.5: Client.java

2 // Set up a Client that will read information sent 3 // from a Server and display the information.

4

5 // Java core packages

6 import java.io.*;

7 import java.net.*;

8import java.awt.*;

9 import java.awt.event.*;

10

11// Java extension packages

12import javax.swing.*;

13

14public class Client extends JFrame {

15private JTextField enterField;

16private JTextArea displayArea;

17private ObjectOutputStream output;

18private ObjectInputStream input;

19private String message = "";

20private String chatServer;

21private Socket client;

22

23// initialize chatServer and set up GUI

24public Client( String host )

25{

26super( "Client" );

27

28// set server to which this client connects

29chatServer = host;

Fig. 17.5 Demonstrating the client portion of a stream-socket connection between a client and a server (part 1 of 5).

Chapter 17

Networking

999

30

31 Container container = getContentPane();

32

33// create enterField and register listener

34enterField = new JTextField();

35enterField.setEnabled( false );

36

 

37

enterField.addActionListener(

38

 

39

new ActionListener() {

40

 

41

// send message to server

42

public void actionPerformed( ActionEvent event )

43

{

44

sendData( event.getActionCommand() );

45

}

46

 

47

} // end anonymous inner class

48

 

49

); // end call to addActionListener

50

 

51

container.add( enterField, BorderLayout.NORTH );

52

 

53// create displayArea

54displayArea = new JTextArea();

55container.add( new JScrollPane( displayArea ),

56

BorderLayout.CENTER );

57

 

58setSize( 300, 150 );

59setVisible( true );

60}

61

62// connect to server and process messages from server

63public void runClient()

64{

65// connect to server, get streams, process connection

66try {

67

 

68

// Step 1: Create a Socket to make connection

69

connectToServer();

70

 

71

// Step 2: Get the input and output streams

72

getStreams();

73

 

74

// Step 3: Process connection

75

processConnection();

76

 

77

// Step 4: Close connection

78closeConnection();

79}

80

Fig. 17.5 Demonstrating the client portion of a stream-socket connection between a client and a server (part 2 of 5).

1000

Networking

Chapter 17

81// server closed connection

82catch ( EOFException eofException ) {

83System.out.println( "Server terminated connection" );

84}

85

86// process problems communicating with server

87catch ( IOException ioException ) {

88ioException.printStackTrace();

89}

90}

91

92// get streams to send and receive data

93private void getStreams() throws IOException

94{

95// set up output stream for objects

96output = new ObjectOutputStream(

97

client.getOutputStream() );

98

 

 

99

// flush output

buffer to send header information

100

output.flush();

 

101

 

 

102// set up input stream for objects

103input = new ObjectInputStream(

104

client.getInputStream() );

105

 

106displayArea.append( "\nGot I/O streams\n" );

107}

108

109// connect to server

110private void connectToServer() throws IOException

111{

112displayArea.setText( "Attempting connection\n" );

114// create Socket to make connection to server

115client = new Socket(

116 InetAddress.getByName( chatServer ), 5000 );

117

118// display connection information

119displayArea.append( "Connected to: " +

120client.getInetAddress().getHostName() );

121}

122

123// process connection with server

124private void processConnection() throws IOException

125{

126// enable enterField so client user can send messages

127enterField.setEnabled( true );

128

129// process messages sent from server

130do {

131

Fig. 17.5 Demonstrating the client portion of a stream-socket connection between a client and a server (part 3 of 5).

Chapter 17

Networking

1001

 

 

 

 

132

 

// read message and display it

 

133

 

try {

 

134

 

message = ( String ) input.readObject();

 

135

 

displayArea.append( "\n" + message );

 

136

 

displayArea.setCaretPosition(

 

137

 

displayArea.getText().length() );

 

138

 

}

 

139

 

 

 

140

 

// catch problems reading from server

 

141

 

catch ( ClassNotFoundException classNotFoundException ) {

142

 

displayArea.append( "\nUnknown object type received" );

143

 

}

 

144

 

 

 

145

} while ( !message.equals( "SERVER>>> TERMINATE" ) );

 

146

 

 

 

147

} // end method process connection

 

148

 

 

 

149// close streams and socket

150private void closeConnection() throws IOException

151{

152displayArea.append( "\nClosing connection" );

153output.close();

154input.close();

155client.close();

156}

157

158// send message to server

159private void sendData( String message )

160{

161// send object to server

162try {

163

output.writeObject( "CLIENT>>> " + message );

164

output.flush();

165displayArea.append( "\nCLIENT>>>" + message );

166}

167

168// process problems sending object

169catch ( IOException ioException ) {

170displayArea.append( "\nError writing object" );

171}

172}

173

174// execute application

175public static void main( String args[] )

176{

177Client application;

178

179 if ( args.length == 0 )

180application = new Client( "127.0.0.1" );

181else

182 application = new Client( args[ 0 ] );

183

Fig. 17.5 Demonstrating the client portion of a stream-socket connection between a client and a server (part 4 of 5).

1002

Networking

Chapter 17

 

 

 

 

184

 

application.setDefaultCloseOperation(

 

185

 

JFrame.EXIT_ON_CLOSE );

 

186

 

 

 

187

 

application.runClient();

 

188

 

}

 

189

 

 

 

190

}

// end class Client

 

The Server and Client windows after the Client connects to the Server

The Server and Client windows after the Client sends a message to the Server

The Server and Client windows after the Server sends a message to the Client

The Server and Client windows after the Client terminates the connection

Fig. 17.5 Demonstrating the client portion of a stream-socket connection between a client and a server (part 5 of 5).

Client method runClient (lines 63–90) performs the work necessary to connect to the Server, to receive data from the Server and to send data to the Server. Line 69 calls method connectToServer (lines 110–121) to perform the connection. After connecting, line 72 calls method getStreams (lines 93–107) to obtain references to the Socket’s InputStream and OutputStream objects. Then line 75 calls method processConnection (124–147) to handle messages sent from the server. When the connection terminates, line 78 calls closeConnection to close the streams and the

Socket.

Method connectToServer (lines 110–121) creates a Socket called client (lines 115–116) to establish a connection. The method passes two arguments to the

Chapter 17

Networking

1003

Socket constructor—the Internet address of the server computer and the port number (5000) where that computer is awaiting client connections. The call to InetAddress static method getByName in the first argument returns an InetAddress object containing the Internet address specified as a command-line argument to the application (or 127.0.0.1 if no command-line arguments are specified). Method getByName can receive a String containing either the actual Internet address or the host name of the server. The first argument also could have been written other ways. For the localhost address 127.0.0.1, the first argument could be

InetAddress.getByName( "localhost" )

or

InetAddress.getLocalHost()

Also, there are versions of the Socket constructor that receive a String for the Internet address or host name. The first argument could have been specified as "127.0.0.1" or "localhost". [Note: We chose to demonstrate the client/server relationship by connecting between programs executing on the same computer (localhost). Normally, this first argument would be the Internet address of another computer. The InetAddress object for another computer can be obtained by specifying the Internet address or host name of the other computer as the String argument to InetAddress.getByName.]

The Socket constructor’s second argument is the server port number. This number must match the port number at which the server is waiting for connections (called the handshake point). Once the connection is made, a message is displayed in the JTextArea (lines 119–120) indicating the name of the server computer to which the client connected.

The Client uses an ObjectOutputStream to send data to the server and an ObjectInputStream to receive data from the server. Method getStreams (lines 93–107) creates the ObjectOutputStream and ObjectInputStream objects that use the OutputStream and InputStream objects associated with client.

Method processConnection (lines 124–147) contains a do/while structure that loops until the client receives the message “SERVER>>> TERMINATE.” Line 134 uses

ObjectInputStream method readObject to read a String from the server. Line 135 displays the message in the JTextArea. Lines 136–137 use JTextComponent method setCaretPosition to position the input cursor in the JTextArea after the last character in the JTextArea.

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

When the user of the client application enters a String in the JTextField and presses the Enter key, the program calls method actionPerformed (lines 42–45) to read the String from the JTextField and invoke utility method sendData (159– 172). Method sendData sends the String object to server client, flushes the output buffer and appends the same String to the JTextArea in the client window.

17.7 Connectionless Client/Server Interaction with Datagrams

We have been discussing connection-oriented, streams-based transmission. Now we consider connectionless transmission with datagrams.

1004

Networking

Chapter 17

Connection-oriented transmission is like the telephone system in which 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 more like the way mail is carried via the postal service. If a large message will not fit in one envelope, you break it into separate message pieces that you place in separate, sequentially numbered envelopes. Each of the letters is then mailed at the same time. The letters could arrive in order, out of order or not at all (although the last case is rare, it does happen). The person at the receiving end reassembles the message pieces into sequential order before attempting to make sense of the message. If your message is small enough to fit in one envelope, you do not have to worry about the “out-of-sequence” problem, but it is still possible that your message might not arrive. One difference between datagrams and postal mail is that duplicates of datagrams can arrive on the receiving computer.

The programs of Fig. 17.6 and Fig. 17.7 use datagrams to send packets of information between a client application and a server application. In the Client application (Fig. 17.7), the user types a message into a JTextField and presses Enter. The program converts the message into a byte array and places it in a datagram packet that is sent to the server. The Server (Fig. 17.6) receives the packet and displays the information in the packet, then echoes the packet back to the client. When the client receives the packet, the client displays the information in the packet. In this example, the Client and Server classes are implemented similarly.

Class Server (Fig. 17.6) defines two DatagramPackets that the server uses to send and receive information and one DatagramSocket that sends and receives these packets. The constructor for class Server (lines 20–41) creates the graphical user interface where the packets of information will be displayed. Next the constructor creates the DatagramSocket in a try block. Line 32 uses the DatagramSocket constructor that takes an integer port number argument (5000) to bind the server to a port where the server can receive packets from clients. Clients sending packets to this Server specify port 5000 in the packets they send. The DatagramSocket constructor throws a SocketException if it fails to bind the DatagramSocket to a port.

Common Programming Error 17.2

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

1// Fig. 17.6: Server.java

2 // Set up a Server that will receive packets from a 3 // client and send packets to a client.

4

5 // Java core packages

6 import java.io.*;

7 import java.net.*;

8import java.awt.*;

9 import java.awt.event.*;

10

Fig. 17.6 Demonstrating the server side of connectionless client/server computing with datagrams (part 1 of 4).

Chapter 17

Networking

1005

11// Java extension packages

12import javax.swing.*;

13

14public class Server extends JFrame {

15private JTextArea displayArea;

16private DatagramPacket sendPacket, receivePacket;

17private DatagramSocket socket;

18

19// set up GUI and DatagramSocket

20public Server()

21{

22super( "Server" );

23

24displayArea = new JTextArea();

25getContentPane().add( new JScrollPane( displayArea ),

26BorderLayout.CENTER );

27setSize( 400, 300 );

28setVisible( true );

29

30// create DatagramSocket for sending and receiving packets

31try {

32socket = new DatagramSocket( 5000 );

33}

34

35// process problems creating DatagramSocket

36catch( SocketException socketException ) {

37 socketException.printStackTrace();

38System.exit( 1 );

39}

40

41 } // end Server constructor

42

43// wait for packets to arrive, then display data and echo

44// packet to client

45public void waitForPackets()

46{

47// loop forever

48while ( true ) {

49

 

50

// receive packet, display contents, echo to client

51

try {

52

 

53

// set up packet

54

byte data[] = new byte[ 100 ];

55

receivePacket =

56

new DatagramPacket( data, data.length );

57

 

58

// wait for packet

59

socket.receive( receivePacket );

60

 

61

// process packet

62

displayPacket();

Fig. 17.6 Demonstrating the server side of connectionless client/server computing with datagrams (part 2 of 4).

1006

Networking

Chapter 17

 

 

 

63

 

 

64

// echo information from packet back to client

65

sendPacketToClient();

 

66

}

 

67

 

 

68

// process problems manipulating packet

 

69

catch( IOException ioException ) {

 

70

displayArea.append( ioException.toString() + "\n" );

71

ioException.printStackTrace();

 

72

}

 

73

 

 

74

} // end while

 

75

 

 

76

} // end method waitForPackets

 

77

 

 

78// display packet contents

79private void displayPacket()

80{

81displayArea.append( "\nPacket received:" +

82

"\nFrom host: " + receivePacket.getAddress() +

83

"\nHost port: " + receivePacket.getPort() +

84

"\nLength: " + receivePacket.getLength() +

85

"\nContaining:\n\t" +

86

new String( receivePacket.getData(), 0,

87

receivePacket.getLength() ) );

88

}

89

 

90// echo packet to client

91private void sendPacketToClient() throws IOException

92{

93displayArea.append( "\n\nEcho data to client..." );

95// create packet to send

96sendPacket = new DatagramPacket( receivePacket.getData(),

97

receivePacket.getLength(),

receivePacket.getAddress(),

98

receivePacket.getPort() );

 

99

 

 

100// send packet

101socket.send( sendPacket );

103displayArea.append( "Packet sent\n" );

104displayArea.setCaretPosition(

105displayArea.getText().length() );

106}

107

108// execute application

109public static void main( String args[] )

110{

111Server application = new Server();

113

application.setDefaultCloseOperation(

114

JFrame.EXIT_ON_CLOSE );

Fig. 17.6 Demonstrating the server side of connectionless client/server computing with datagrams (part 3 of 4).

Chapter 17

Networking

1007

115

116application.waitForPackets();

117}

118

119 } // end class Server

The Server window after the client sends a packet of data

Fig. 17.6 Demonstrating the server side of connectionless client/server computing with datagrams (part 4 of 4).

Server method waitForPackets (lines 45–76) uses an infinite loop to wait for packets to arrive at the Server. Lines 54–56 create a DatagramPacket in which a received packet of information can be stored. The DatagramPacket constructor for this purpose receives two arguments—a byte array containing the data and the length of the byte array. Line 59 waits for a packet to arrive at the Server. Method receive blocks until a packet arrives, then stores the packet in its DatagramPacket argument. Method receive throws an IOException if an error occurs receiving a packet.

When a packet arrives, the program calls method displayPacket (lines 79–88) to append the packet’s contents to displayArea. DatagramPacket method getAddress (line 82) returns an InetAddress object containing the host name of the computer from which the packet was sent. Method getPort (line 83) returns an integer specifying the port number through which the host computer sent the packet. Method getLength (line 84) returns an integer representing the number of bytes of data that were sent. Method getData (line 86) returns a byte array containing the data that was sent. The program uses the byte array to initialize a String object so the data can be output to the JTextArea.

After displaying a packet, the program calls method sendPacketToClient (line 65) to create a new packet and send it to the client. Lines 96–98 create sendPacket and pass four arguments to the DatagramPacket constructor. The first argument specifies the byte array to send. The second argument specifies the number of bytes to send. The third argument specifies the client computer’s Internet address, to which the packet will be sent. The fourth argument specifies the port where the client is waiting to receive packets. Line 101 sends the packet over the network. Method send throws an IOException if an error occurs sending a packet.

Class Client (Fig. 17.7) works similarly to class Server, except that the Client sends packets only when the user types a message in a JTextField and presses the Enter

1008

Networking

Chapter 17

key. When this occurs, the program calls method actionPerformed (lines 34–67), which converts the String the user entered in the JTextField into a byte array (line 45). Lines 48–50 create a DatagramPacket and initialize it with the byte array, the length of the String that was entered by the user, the Internet address to which the packet is to be sent (InetAddress.getLocalHost() in this example) and the port number at which the Server is waiting for packets. Line 53 sends the packet. Note that the client in this example must know that the server is receiving packets at port 5000; otherwise, the server will not receive the packets.

1// Fig. 17.7: Client.java

2 // Set up a Client that will send packets to a 3 // server and receive packets from a server.

4

5 // Java core packages

6 import java.io.*;

7 import java.net.*;

8import java.awt.*;

9 import java.awt.event.*;

10

11// Java extension packages

12import javax.swing.*;

13

14public class Client extends JFrame {

15private JTextField enterField;

16private JTextArea displayArea;

17private DatagramPacket sendPacket, receivePacket;

18private DatagramSocket socket;

19

20// set up GUI and DatagramSocket

21public Client()

22{

23super( "Client" );

24

 

25

Container container = getContentPane();

26

 

27

enterField = new JTextField( "Type message here" );

28

 

29

enterField.addActionListener(

30

 

31

new ActionListener() {

32

 

33

// create and send a packet

34

public void actionPerformed( ActionEvent event )

35

{

36

// create and send packet

37

try {

38

displayArea.append(

39

"\nSending packet containing: " +

40

event.getActionCommand() + "\n" );

41

 

Fig. 17.7 Demonstrating the client side of connectionless client/server computing with datagrams (part 1 of 4).

Chapter 17

Networking

1009

 

 

 

42

 

// get message from textfield and convert to

43

 

// array of bytes

 

44

 

String message = event.getActionCommand();

 

45

 

byte data[] = message.getBytes();

 

46

 

 

 

47

 

// create sendPacket

 

48

 

sendPacket = new DatagramPacket(

 

49

 

data, data.length,

 

50

 

InetAddress.getLocalHost(), 5000 );

 

51

 

 

 

52

 

// send packet

 

53

 

socket.send( sendPacket );

 

54

 

 

 

55

 

displayArea.append( "Packet sent\n" );

 

56

 

displayArea.setCaretPosition(

 

57

 

displayArea.getText().length() );

 

58

 

}

 

59

 

 

 

60

 

// process problems creating or sending packet

61

 

catch ( IOException ioException ) {

 

62

 

displayArea.append(

 

63

 

ioException.toString() + "\n" );

 

64

 

ioException.printStackTrace();

 

65

 

}

 

66

 

 

 

67

 

} // end actionPerformed

 

68

 

 

 

69

}

// end anonymous inner class

 

70

 

 

 

71

); // end call to addActionListener

 

72

 

 

 

73

container.add( enterField, BorderLayout.NORTH );

 

74

 

 

 

75displayArea = new JTextArea();

76container.add( new JScrollPane( displayArea ),

77

BorderLayout.CENTER );

78

 

79setSize( 400, 300 );

80setVisible( true );

82// create DatagramSocket for sending and receiving packets

83try {

84socket = new DatagramSocket();

85}

86

87// catch problems creating DatagramSocket

88catch( SocketException socketException ) {

89 socketException.printStackTrace();

90System.exit( 1 );

91}

92

93 } // end Client constructor

Fig. 17.7 Demonstrating the client side of connectionless client/server computing with datagrams (part 2 of 4).

1010

Networking

Chapter 17

94

95// wait for packets to arrive from Server,

96// then display packet contents

97public void waitForPackets()

98{

99// loop forever

100

while ( true ) {

101

 

102

// receive packet and display contents

103

try {

104

 

105

// set up packet

106

byte data[] = new byte[ 100 ];

107

receivePacket =

108

new DatagramPacket( data, data.length );

109

 

110

// wait for packet

111

socket.receive( receivePacket );

112

 

113

// display packet contents

114

displayPacket();

115

}

116

 

117

// process problems receiving or displaying packet

118

catch( IOException exception ) {

119

displayArea.append( exception.toString() + "\n" );

120

exception.printStackTrace();

121

}

122

 

123

} // end while

124

 

125

} // end method waitForPackets

126

 

127// display contents of receivePacket

128private void displayPacket()

129{

130displayArea.append( "\nPacket received:" +

131

"\nFrom host: " + receivePacket.getAddress() +

132

"\nHost port: " + receivePacket.getPort() +

133

"\nLength: " + receivePacket.getLength() +

134

"\nContaining:\n\t" +

135

new String( receivePacket.getData(), 0,

136

receivePacket.getLength() ) );

137

 

138

displayArea.setCaretPosition(

139displayArea.getText().length() );

140}

141

142// execute application

143public static void main( String args[] )

144{

145Client application = new Client();

Fig. 17.7 Demonstrating the client side of connectionless client/server computing with datagrams (part 3 of 4).

Chapter 17

Networking

1011

 

 

 

 

146

 

 

 

147

application.setDefaultCloseOperation(

 

 

148

JFrame.EXIT_ON_CLOSE );

 

 

149

 

 

 

150application.waitForPackets();

151}

152

153 } // end class Client

The Client window after sending a packet to the server and receiving the packet back from the server

Fig. 17.7 Demonstrating the client side of connectionless client/server computing with datagrams (part 4 of 4).

Notice that the DatagramSocket constructor call (line 84) in this application does not specify any arguments. This constructor allows the computer to select the next available port number for the DatagramSocket. The client does not need a specific port number, because the server receives the client’s port number as part of each DatagramPacket sent by the client. Thus, the server can send packets back to the same computer and port number from which the server receives a packet of information.

Client method waitForPackets (lines 97–125) uses an infinite loop to wait for packets from the server. Line 111 blocks until a packet arrives. Note that this does not prevent the user from sending a packet, because the GUI events are handled in the event dispatch thread. It only prevents the while loop from continuing until a packet arrives at the Client. When a packet arrives, line 111 stores the packet in receivePacket, and line 114 calls method displayPacket (128–140) to display the packet’s contents in the

JTextArea.

17.8 Client/Server Tic-Tac-Toe Using a Multithreaded Server

In this section, we present the popular game Tic-Tac-Toe implemented by using client/server techniques with stream sockets. The program consists of a TicTacToeServer application (Fig. 17.8) that allows two TicTacToeClient applets (Fig. 17.9) to connect to the server and play Tic-Tac-Toe (outputs shown in Fig. 17.10). As the server receives each client connection, it creates an instance of inner class Player (lines 158–279 of Fig. 17.8) to process the client in a separate thread. These threads enable the clients to play the game independently. The server assigns Xs to the first client to connect (X makes the first move) and assigns

1012

Networking

Chapter 17

Os to the second client to connect. The server maintains the information about the board so it can determine whether a player’s move is a valid or invalid move. Each TicTacToeClient applet (Fig. 17.9) maintains its own GUI version of the Tic-Tac-Toe board on which it displays the state of the game. The clients can place a mark only in an empty square on the board. Class Square (lines 212–270 of Fig. 17.9) implements each of the nine squares on the board. Class TicTacToeServer and class Player are implemented in file TicTacToeServer.java (Fig. 17.8). Class TicTacToeClient and class Square are implemented in file TicTacToeClient.java (Fig. 17.9).

1// Fig. 17.8: TicTacToeServer.java

2 // This class maintains a game of Tic-Tac-Toe for two 3 // client applets.

4

5 // Java core packages

6import java.awt.*;

7 import java.awt.event.*;

8 import java.net.*;

9 import java.io.*;

10

11// Java extension packages

12import javax.swing.*;

13

14public class TicTacToeServer extends JFrame {

15private byte board[];

16private JTextArea outputArea;

17private Player players[];

18private ServerSocket server;

19private int currentPlayer;

20

21// set up tic-tac-toe server and GUI that displays messages

22public TicTacToeServer()

23{

24super( "Tic-Tac-Toe Server" );

25

26board = new byte[ 9 ];

27players = new Player[ 2 ];

28currentPlayer = 0;

29

30// set up ServerSocket

31try {

32server = new ServerSocket( 5000, 2 );

33}

34

35// process problems creating ServerSocket

36catch( IOException ioException ) {

37 ioException.printStackTrace();

38System.exit( 1 );

39}

40

41// set up JTextArea to display messages during execution

42outputArea = new JTextArea();

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 1 of 6).

Chapter 17

Networking

1013

43getContentPane().add( outputArea, BorderLayout.CENTER );

44outputArea.setText( "Server awaiting connections\n" );

46setSize( 300, 300 );

47setVisible( true );

48}

49

50// wait for two connections so game can be played

51public void execute()

52{

53// wait for each client to connect

54for ( int i = 0; i < players.length; i++ ) {

56

// wait for connection, create Player, start thread

57

try {

58

players[ i ] = new Player( server.accept(), i );

59

players[ i ].start();

60

}

61

 

62

// process problems receiving connection from client

63

catch( IOException ioException ) {

64

ioException.printStackTrace();

65

System.exit( 1 );

66}

67}

68

69// Player X is suspended until Player O connects.

70// Resume player X now.

71synchronized ( players[ 0 ] ) {

72

players[ 0 ].setSuspended( false );

73players[ 0 ].notify();

74}

75

76 } // end method execute

77

78// display a message in outputArea

79public void display( String message )

80{

81outputArea.append( message + "\n" );

82}

83

84// Determine if a move is valid.

85// This method is synchronized because only one move can be

86// made at a time.

87public synchronized boolean validMove(

88int location, int player )

89{

90boolean moveDone = false;

91

92// while not current player, must wait for turn

93while ( player != currentPlayer ) {

94

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 2 of 6).

1014

Networking

Chapter 17

95

// wait for turn

96

try {

97

wait();

98

}

99

 

100

// catch wait interruptions

101

catch( InterruptedException interruptedException ) {

102

interruptedException.printStackTrace();

103}

104}

106// if location not occupied, make move

107if ( !isOccupied( location ) ) {

108

 

109

// set move in board array

110

board[ location ] =

111

( byte ) ( currentPlayer == 0 ? 'X' : 'O' );

112

 

113

// change current player

114

currentPlayer = ( currentPlayer + 1 ) % 2;

115

 

116

// let new current player know that move occurred

117

players[ currentPlayer ].otherPlayerMoved( location );

118

 

119

// tell waiting player to continue

120

notify();

121

 

122

// tell player that made move that the move was valid

123return true;

124}

125

126// tell player that made move that the move was not valid

127else

128return false;

129}

130

131// determine whether location is occupied

132public boolean isOccupied( int location )

133{

134if ( board[ location ] == 'X' || board [ location ] == 'O' )

135return true;

136else

137return false;

138}

139

140// place code in this method to determine whether game over

141public boolean gameOver()

142{

143return false;

144}

145

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 3 of 6).

Chapter 17

Networking

1015

146// execute application

147public static void main( String args[] )

148{

149TicTacToeServer application = new TicTacToeServer();

151

application.setDefaultCloseOperation(

152

JFrame.EXIT_ON_CLOSE );

153

 

154application.execute();

155}

156

157// private inner class Player manages each Player as a thread

158private class Player extends Thread {

159private Socket connection;

160private DataInputStream input;

161private DataOutputStream output;

162private int playerNumber;

163private char mark;

164protected boolean suspended = true;

165

166// set up Player thread

167public Player( Socket socket, int number )

168{

169

playerNumber = number;

170

 

171

// specify player's mark

172

mark = ( playerNumber == 0 ? 'X' : 'O' );

173

 

174

connection = socket;

175

 

176

// obtain streams from Socket

177

try {

178

input = new DataInputStream(

179

connection.getInputStream() );

180

output = new DataOutputStream(

181

connection.getOutputStream() );

182

}

183

 

184

// process problems getting streams

185

catch( IOException ioException ) {

186

ioException.printStackTrace();

187

System.exit( 1 );

188}

189}

191// send message that other player moved; message contains

192// a String followed by an int

193public void otherPlayerMoved( int location )

194{

195

// send message indicating move

196

try {

197

output.writeUTF( "Opponent moved" );

 

 

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 4 of 6).

1016

Networking

Chapter 17

 

 

 

198

output.writeInt( location );

 

199

}

 

200

 

 

201

// process problems sending message

 

202

catch ( IOException ioException ) {

 

203

ioException.printStackTrace();

 

204}

205}

207// control thread's execution

208public void run()

209{

210

// send client message indicating its mark (X or O),

211

// process messages from client

212

try {

213

display( "Player " + ( playerNumber == 0 ?

214

'X' : 'O' ) + " connected" );

215

 

216

// send player's mark

217

output.writeChar( mark );

218

 

219

// send message indicating connection

220

output.writeUTF( "Player " +

221

( playerNumber == 0 ? "X connected\n" :

222

"O connected, please wait\n" ) );

223

 

224

// if player X, wait for another player to arrive

225

if ( mark == 'X' ) {

226

output.writeUTF( "Waiting for another player" );

227

 

228

// wait for player O

229

try {

230

synchronized( this ) {

231

while ( suspended )

232

wait();

233

}

234

}

235

 

236

// process interruptions while waiting

237

catch ( InterruptedException exception ) {

238

exception.printStackTrace();

239

}

240

 

241

// send message that other player connected and

242

// player X can make a move

243

output.writeUTF(

244

"Other player connected. Your move." );

245

}

246

 

247

// while game not over

248

while ( ! gameOver() ) {

249

 

 

 

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 5 of 6).

Chapter 17

Networking

1017

 

 

 

250

// get move location from client

 

251

int location = input.readInt();

 

252

 

 

253

// check for valid move

 

254

if ( validMove( location, playerNumber ) ) {

 

255

display( "loc: " + location );

 

256

output.writeUTF( "Valid move." );

 

257

}

 

258

else

 

259

output.writeUTF( "Invalid move, try again" );

260

}

 

261

 

 

262

// close connection to client

 

263

connection.close();

 

264

}

 

265

 

 

266

// process problems communicating with client

 

267

catch( IOException ioException ) {

 

268

ioException.printStackTrace();

 

269

System.exit( 1 );

 

270}

271}

273// set whether or not thread is suspended

274public void setSuspended( boolean status )

275{

276suspended = status;

277}

278

279 } // end class Player

280

281 } // end class TicTacToeServer

Fig. 17.8 Server side of client/server Tic-Tac-Toe program (part 6 of 6).

We begin with a discussion of the server side of the Tic-Tac-Toe game. When the TicTacToeServer application executes, the main method (lines 147–155) creates a TicTacToeServer object called application. The constructor (lines 22–48) attempts to set up a ServerSocket. If successful, the program displays the server

1018

Networking

Chapter 17

window, and main invokes the TicTacToeServer method execute (lines 51–76). Method execute loops twice, blocking at line 58 each time while waiting for a client connection. When a client connects, line 58 creates a new Player object to manage the connection as a separate thread, and line 59 calls that object’s start method to begin executing the thread.

When the TicTacToeServer creates a Player, the Player constructor (lines 167–189) receives the Socket object representing the connection to the client and gets the associated input and output streams. The Player’s run method (lines 208–271) controls the information that is sent to the client and the information that is received from the client. First, it tells the client that the client’s connection has been made (lines 213–214), then it passes to the client the character that the client will place on the board when a move is made (line 217). Lines 230–233 suspend each Player thread as it starts executing, because neither player is allowed to make a move when it first connects. Player X can move only when player O connects, and player O can make a move only after player X.

At this point, the game can be played, and the run method begins executing its while structure (lines 248–260). Each iteration of this while structure reads an integer (line 253) representing the location where the client wants to place a mark, and line 254 invokes the TicTacToeServer method validMove (lines 87–129) to check the move. Lines 254– 259 send a message to the client indicating whether the move was valid. The program maintains board locations as numbers from 0 to 8 (0 through 2 for the first row, 3 through 5 for the second row and 6 through 8 for the third row).

Method validMove (lines 87–129 in class TicTacToeServer) is a synchronized method that allows only one player at a time to move. Synchronizing validMove prevents both players from modifying the state information of the game simultaneously. If the Player attempting to validate a move is not the current player (i.e., the one allowed to make a move), the Player is placed in a wait state until it is that Player’s turn to move. If the position for the move being validated is already occupied on the board, validMove returns false. Otherwise, the server places a mark for the player in its local representation of the board (lines 110–111), notifies the other Player object (line 117) that a move has been made (so the client can be sent a message), invokes method notify (line 120) so the waiting Player (if there is one) can validate a move and returns true (line 123) to indicate that the move is valid.

When a TicTacToeClient (Fig. 17.9) applet begins execution, it creates a JTextArea in which messages from the server and a representation of the board using nine Square objects are displayed. The applet’s start method (lines 80–104) opens a connection to the server and gets the associated input and output streams from the Socket object. Class TicTacToeClient implements interface Runnable so that a separate thread can read messages from the server. This approach enables the user to interact with the board (in the event-dispatch thread) while waiting for messages from the server. After establishing the connection to the server, line 102 creates Thread object outputThread and initializes it with the Runnable applet, then line 103 calls the thread’s start method. The applet’s run method (lines 108–137) controls the separate thread of execution. The method first reads the mark character (X or O) from the server (line 112), then loops continually (lines 123–135) and reads messages from the server (line 127). Each message is passed to the applet’s processMessage method (lines 140–211) for processing.

Chapter 17

Networking

1019

 

 

 

 

1

// Fig. 17.9:

TicTacToeClient.java

 

2

//

Client for

the TicTacToe program

 

3

 

 

 

 

4

//

Java core packages

 

5import java.awt.*;

6 import java.awt.event.*;

7 import java.net.*;

8 import java.io.*;

9

10// Java extension packages

11import javax.swing.*;

12

13// Client class to let a user play Tic-Tac-Toe with

14// another user across a network.

15public class TicTacToeClient extends JApplet

16implements Runnable {

17

18private JTextField idField;

19private JTextArea displayArea;

20private JPanel boardPanel, panel2;

21private Square board[][], currentSquare;

22private Socket connection;

23private DataInputStream input;

24private DataOutputStream output;

25private Thread outputThread;

26private char myMark;

27private boolean myTurn;

28

29// Set up user-interface and board

30public void init()

31{

32Container container = getContentPane();

34// set up JTextArea to display messages to user

35displayArea = new JTextArea( 4, 30 );

36displayArea.setEditable( false );

37container.add( new JScrollPane( displayArea ),

38

BorderLayout.SOUTH );

39

 

40// set up panel for squares in board

41boardPanel = new JPanel();

42boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );

44// create board

45board = new Square[ 3 ][ 3 ];

47// When creating a Square, the location argument to the

48// constructor is a value from 0 to 8 indicating the

49// position of the Square on the board. Values 0, 1,

50// and 2 are the first row, values 3, 4, and 5 are the

51// second row. Values 6, 7, and 8 are the third row.

52for ( int row = 0; row < board.length; row++ ) {

53

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 1 of 6).

1020

Networking

Chapter 17

 

 

 

54

for ( int column = 0;

 

55

column < board[ row ].length; column++ ) {

56

 

 

57

// create Square

 

58

board[ row ][ column ] =

 

59

new Square( ' ', row * 3 + column );

 

60

 

 

61

boardPanel.add( board[ row ][ column ] );

 

62

}

 

63

 

 

64

}

 

65

 

 

66// textfield to display player's mark

67idField = new JTextField();

68idField.setEditable( false );

69container.add( idField, BorderLayout.NORTH );

71// set up panel to contain boardPanel (for layout purposes)

72panel2 = new JPanel();

73panel2.add( boardPanel, BorderLayout.CENTER );

74container.add( panel2, BorderLayout.CENTER );

75}

76

77// Make connection to server and get associated streams.

78// Start separate thread to allow this applet to

79// continually update its output in text area display.

80public void start()

81{

82// connect to server, get streams and start outputThread

83try {

84

 

85

// make connection

86

connection = new Socket(

87

InetAddress.getByName( "127.0.0.1" ), 5000 );

88

 

89

// get streams

90

input = new DataInputStream(

91

connection.getInputStream() );

92

output = new DataOutputStream(

93

connection.getOutputStream() );

94

}

95

 

96// catch problems setting up connection and streams

97catch ( IOException ioException ) {

98ioException.printStackTrace();

99}

100

101// create and start output thread

102outputThread = new Thread( this );

103outputThread.start();

104}

105

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 2 of 6).

Chapter 17

Networking

1021

106// control thread that allows continuous update of the

107// text area displayArea

108public void run()

109{

110// get player's mark (X or O)

111try {

112 myMark = input.readChar();

113 idField.setText( "You are player \"" + myMark + "\"" );

114myTurn = ( myMark == 'X' ? true : false );

115}

116

117// process problems communicating with server

118catch ( IOException ioException ) {

119ioException.printStackTrace();

120}

121

122// receive messages sent to client and output them

123while ( true ) {

124

 

125

// read message from server and process message

126

try {

127

String message = input.readUTF();

128

processMessage( message );

129

}

130

 

131

// process problems communicating with server

132

catch ( IOException ioException ) {

133

ioException.printStackTrace();

134}

135}

137 } // end method run

138

139// process messages received by client

140public void processMessage( String message )

141{

142// valid move occurred

143if ( message.equals( "Valid move." ) ) {

144

displayArea.append( "Valid move, please wait.\n" );

145

 

146

// set mark in square from event-dispatch thread

147

SwingUtilities.invokeLater(

148

 

149

new Runnable() {

150

 

151

public void run()

152

{

153

currentSquare.setMark( myMark );

154

}

155

 

156

}

157

 

 

 

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 3 of 6).

1022

Networking

Chapter 17

158); // end call to invokeLater

159}

160

161// invalid move occurred

162else if ( message.equals( "Invalid move, try again" ) ) {

163displayArea.append( message + "\n" );

164myTurn = true;

165

}

166

 

167// opponent moved

168else if ( message.equals( "Opponent moved" ) ) {

170

// get move location and update board

171

try {

172

final int location = input.readInt();

173

 

174

// set mark in square from event-dispatch thread

175

SwingUtilities.invokeLater(

176

 

177

new Runnable() {

178

 

179

public void run()

180

{

181

int row = location / 3;

182

int column = location % 3;

183

 

184

board[ row ][ column ].setMark(

185

( myMark == 'X' ? 'O' : 'X' ) );

186

displayArea.append(

187

"Opponent moved. Your turn.\n" );

188

}

189

 

190

}

191

 

192

); // end call to invokeLater

193

 

194

myTurn = true;

195

}

196

 

197

// process problems communicating with server

198

catch ( IOException ioException ) {

199

ioException.printStackTrace();

200

}

201

 

202

}

203

 

204// simply display message

205else

206 displayArea.append( message + "\n" );

207

208 displayArea.setCaretPosition(

209 displayArea.getText().length() );

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 4 of 6).

Chapter 17

Networking

1023

210

211 } // end method processMessage

212

213// send message to server indicating clicked square

214public void sendClickedSquare( int location )

215{

216if ( myTurn ) {

217

 

218

// send location to server

219

try {

220

output.writeInt( location );

221

myTurn = false;

222

}

223

 

224

// process problems communicating with server

225

catch ( IOException ioException ) {

226

ioException.printStackTrace();

227}

228}

229}

230

231// set current Square

232public void setCurrentSquare( Square square )

233{

234currentSquare = square;

235}

236

237// private class for the sqaures on the board

238private class Square extends JPanel {

239private char mark;

240private int location;

241

242public Square( char squareMark, int squareLocation )

243{

244

mark = squareMark;

245

location = squareLocation;

246

 

247

addMouseListener(

248

 

249

new MouseAdapter() {

250

 

251

public void mouseReleased( MouseEvent e )

252

{

253

setCurrentSquare( Square.this );

254

sendClickedSquare( getSquareLocation() );

255

}

256

 

257

} // end anonymous inner class

258

 

259

); // end call to addMouseListener

260

 

261

} // end Square constructor

 

 

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 5 of 6).

1024

Networking

Chapter 17

262

263// return preferred size of Square

264public Dimension getPreferredSize()

265{

266return new Dimension( 30, 30 );

267}

268

269// return minimum size of Square

270public Dimension getMinimumSize()

271{

272return getPreferredSize();

273}

274

275// set mark for Square

276public void setMark( char newMark )

277{

278 mark = newMark;

279repaint();

280}

281

282// return Square location

283public int getSquareLocation()

284{

285return location;

286}

287

288// draw Square

289public void paintComponent( Graphics g )

290{

291 super.paintComponent( g );

292

293 g.drawRect( 0, 0, 29, 29 );

294g.drawString( String.valueOf( mark ), 11, 20 );

295}

296

297 } // end class Square

298

299 } // end class TicTacToeClient

Fig. 17.9 Client side of client/server Tic-Tac-Toe program (part 6 of 6).

If the message received is Valid move., lines 143–159 display the message Valid move, please wait. and call class Square’s setMark method to set the client’s mark in the current square (the one in which the user clicked) using SwingUtilities method invokeLater to ensure that the GUI updates occur in the event dispatch thread. If the message received is Invalid move, try again., lines 162–165 display the message so the user can click a different square. If the message received is Opponent moved., lines 168–195 read an integer from the server indicating where the opponent moved and place a mark in that square of the board (again using SwingUtilities method invokeLater to ensure that the GUI updates occur in the event dispatch thread). If any other message is received, line 206 simply displays the message.

Chapter 17

Networking

1025

 

 

 

 

 

 

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