19. Networking

Objectives

In this chapter you’ll learn:

• To understand Java networking with URLs, sockets and datagrams.

• To implement Java networking applications by using sockets and datagrams.

• To understand how to implement Java clients and servers that communicate with one another.

• To understand how to implement network-based collaborative applications.

• To construct a multithreaded server.

If the presence of electricity can be made visible in any part of a circuit, I see no reason why intelligence may not be transmitted instantaneously by electricity.

Samuel F. B. Morse

Protocol is everything.

Francois Giuliani

What networks of railroads, highways and canals were in another age, the networks of telecommunications, information and computerization ... are today.

Bruno Kreisky

The port is near, the bells I hear, the people all exulting.

Walt Whitman

Outline

19.1   Introduction

19.2   Manipulating URLs

19.3   Reading a File on a Web Server

19.4   Establishing a Simple Server Using Stream Sockets

19.5   Establishing a Simple Client Using Stream Sockets

19.6   Client/Server Interaction with Stream Socket Connections

19.7   Connectionless Client/Server Interaction with Datagrams

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

19.9   Security and the Network

19.10   [Web Bonus] Case Study: DeitelMessenger Server and Client

19.11   Wrap-Up

19.1 Introduction

There is much excitement about the Internet and the World Wide Web. The Internet ties the information world together. The World Wide Web makes the Internet easy to use and gives it the flair and sizzle of multimedia. Organizations see the Internet and the web as crucial to their information-systems strategies. Java provides a number of built-in networking capabilities that make it easy to develop Internet-based and web-based applications. Java can enable programs to search the world for information and to collaborate with programs running on other computers internationally, nationally or just within an organization. Java can enable applets and applications to communicate with one another (subject to security constraints).

Java’s fundamental networking capabilities are declared by classes and interfaces of package java.net, through which Java offers stream-based communications that enable applications to view networking as streams of data. The classes and interfaces of package java.net also offer packet-based communications for transmitting individual packets of information—commonly used to transmit audio and video over the Internet. In this chapter, we show how to create and manipulate sockets and how to communicate with packets and streams of data.

Our discussion of networking focuses on both sides of the client/server relationship. The client requests that some action be performed, and the server performs the action and responds to the client. A common implementation of the request-response model is between web browsers and web servers. When a user selects a website to browse through a browser (the client application), a request is sent to the appropriate web server (the server application). The server normally responds to the client by sending an appropriate HTML web page.

We introduce Java’s socket-based communications, which enable applications to view networking as if it were file I/O—a program can read from a socket or write to a socket as simply as reading from a file or writing to a file. The socket is simply a software construct that represents one endpoint of a connection. We show how to create and manipulate 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 appropriate for everyday programmers, because the protocol used—UDP, the User Datagram Protocol—is a connectionless service, and thus does not guarantee that packets arrive in any particular order. With UDP, packets can even be lost or duplicated. Significant extra programming is required on your part to deal with these problems (if you choose to do so). UDP is most appropriate for network applications that do not require the error checking and reliability of TCP. Stream sockets and the TCP protocol will be more desirable for the vast majority of Java programmers.

Performance Tip 19.1

Image

Connectionless services generally offer greater performance but less reliability than connection-oriented services.

Portability Tip 19.1

Image

TCP, UDP and related protocols enable a great variety of heterogeneous computer systems (i.e., computer systems with different processors and different operating systems) to intercommunicate.

We also introduce a case study in which we implement a client/server chat application similar to the instant-messaging services popular on the web today. This case study is provided as a web bonus at www.deitel.com/books/javafp/. The application incorporates many networking techniques introduced in this chapter. It also introduces multicasting, in which a server can publish information and clients can subscribe to that information. Each time the server publishes more information, all subscribers receive it. Throughout the examples of this chapter, we’ll see that many of the networking details are handled by the Java APIs.

19.2 Manipulating URLs

The Internet offers many protocols. The Hypertext Transfer Protocol (HTTP), which forms the basis of the World Wide Web, uses URIs (Uniform Resource Identifiers) to identify data on the Internet. URIs that specify the locations of documents are called URLs (Uniform Resource Locators). Common URLs refer to files or directories and can reference objects that perform complex tasks, such as database lookups and Internet searches. If you know the HTTP URL of a publicly available HTML document anywhere on the web, you can access it through HTTP.

Java makes it easy to manipulate URLs. When you use a URL that refers to the exact location of a resource (e.g., a web page) as an argument to the showDocument method of interface AppletContext, the browser in which the applet is executing will display that resource. The applet in Figs. 19.119.2 demonstrates simple networking capabilities. It enables the user to select a web page from a JList and causes the browser to display the corresponding page. In this example, the networking is performed by the browser.

Fig. 19.1. HTML document to load SiteSelector applet.

Image

Fig. 19.2. Loading a document from a URL into a browser.

Image

Image

Image

Image

This applet takes advantage of applet parameters specified in the HTML document that invokes the applet. When browsing the World Wide Web, you’ll often come across applets that are in the public domain—you can use them free of charge on your own web pages (normally in exchange for crediting the applet’s creator). Many applets can be customized via parameters supplied from the HTML file that invokes the applet. For example, Fig. 19.1 contains the HTML that invokes the applet SiteSelector in Fig. 19.2.

The HTML document contains eight parameters specified with the param tag—these lines must appear between the starting and ending applet tags. The applet can read these values and use them to customize itself. Any number of param tags can appear between the starting and ending applet tags. Each parameter has a name and a value. Applet method getParameter retrieves the value associated with a specific parameter name and returns it as a string. The argument passed to getParameter is a string containing the name of the parameter in the param element. In this example, parameters represent the title and location of each website the user can select. Parameters specified for this applet are named title#, where the value of # starts at 0 and increments by 1 for each new title. Each title should have a corresponding location parameter of the form location#, where the value of # starts at 0 and increments by 1 for each new location. The statement

String title = getParameter( "title0" );

gets the value associated with parameter "title0" and assigns it to reference title. If there is no param tag containing the specified parameter, getParameter returns null.

The applet (Fig. 19.2) obtains from the HTML document (Fig. 19.1) the choices that will be displayed in the applet’s JList. Class SiteSelector uses a HashMap (package java.util) to store the website names and URLs. In this example, the key is the string in the JList that represents the website name, and the value is a URL object that stores the location of the website to display in the browser.

Class SiteSelector also contains an ArrayList (package java.util) in which the site names are placed so that they can be used to initialize the JList (one version of the JList constructor receives an array of Objects which is returned by ArrayList’s toArray method). An ArrayList is a dynamically resizable array of references. Class ArrayList provides method add to add a new element to the end of the ArrayList. (We provide discussions of classes ArrayList and HashMap in Chapter 16.)

Lines 25–26 in the applet’s init method (lines 23–57) create a HashMap object and an ArrayList object. Line 29 calls our utility method getSitesFromHTMLParameters (declared at lines 60–89) to obtain the HTML parameters from the HTML document that invoked the applet.

Method getSitesFromHTMLParameters uses Applet method getParameter (line 67) to obtain a website title. If the title is not null, the loop in lines 70–88 begins executing. Line 73 uses Applet method getParameter to obtain the website location. Line 77 uses the location as the value of a new URL object. The URL constructor determines whether its argument represents a valid URL. If not, the URL constructor throws a MalformedURLException. Note that the URL constructor must be called in a try block. If the URL constructor generates a MalformedURLException, the call to printStackTrace (line 83) causes the program to output a stack trace to the Java console. On Windows machines, the Java console can be viewed by right clicking the Java icon in the notification area of the taskbar. Then the program attempts to obtain the next website title. The program does not add the site for the invalid URL to the HashMap, so the title will not be displayed in the JList.

For a proper URL, line 78 places the title and URL into the HashMap, and line 79 adds the title to the ArrayList. Line 87 gets the next title from the HTML document. When the call to getParameter at line 87 returns null, the loop terminates.

When method getSitesFromHTMLParameters returns to init, lines 32–56 construct the applet’s GUI. Line 32 adds the JLabelChoose a site to browse” to the NORTH of the JFrame’s BorderLayout. Line 34 creates JList siteChooser to allow the user to select a web page to view. Lines 35–54 register a ListSelectionListener to handle the siteChooser’s events. Line 56 adds siteChooser to the CENTER of the BorderLayout.

When the user selects one of the websites listed in siteChooser, the program calls method valueChanged (lines 39–52). Line 42 obtains the selected site name from the JList. Line 45 passes the selected site name (the key) to HashMap method get, which locates and returns a reference to the corresponding URL object (the value) that is assigned to reference newDocument.

Line 48 uses Applet method getAppletContext to get a reference to an AppletContext object that represents the applet container. Line 51 uses the AppletContext reference browser to invoke method showDocument, which receives a URL object as an argument and passes it to the AppletContext (i.e., the browser). The browser displays in the current browser window the World Wide Web resource associated with that URL. In this example, all the resources are HTML documents.

For programmers familiar with HTML frames, there is a second version of AppletContext method showDocument that enables an applet to specify the so-called target frame in which to display the web resource. This second version takes two arguments—a URL object specifying the resource to display and a string representing the target frame. There are some special target frames that can be used as the second argument. The target frame _blank results in a new web browser window to display the content from the specified URL. The target frame _self specifies that the content from the specified URL should be displayed in the same frame as the applet (the applet’s HTML page is replaced in this case). The target frame _top specifies that the browser should remove the current frames in the browser window, then display the content from the specified URL in the current window. [Note: If you are interested in learning more about HTML, please visit our Resource Centers on extensible hypertext markup language (XHTML; www.deitel.com/xhtml/) and the web page formatting capability called Cascading Style Sheets (CSS; www.deitel.com/css21/).]

Error-Prevention Tip 19.1

Image

The applet in Fig. 19.2 must be run from a web browser, such as Mozilla or Microsoft Internet Explorer, to see the results of displaying another web page. The appletviewer is capable only of executing applets—it ignores all other HTML tags. If the websites in the program contained Java applets, only those applets would appear in the appletviewer when the user selected a website. Each applet would execute in a separate appletviewer window.

19.3 Reading a File on a Web Server

Our next example once again hides the networking details from us. The application in Fig. 19.3 uses Swing GUI component JEditorPane (from package javax.swing) to display the contents of a file on a web server. The user enters a URL in the JTextField at the top of the window, and the application displays the corresponding document (if it exists) in the JEditorPane. Class JEditorPane is able to render both plain text and HTML-formatted text, as illustrated in the two screen captures (Fig. 19.4), so this application acts as a simple web browser. The application also demonstrates how to process HyperlinkEvents when the user clicks a hyperlink in the HTML document. The techniques shown in this example can also be used in applets. However, an applet is allowed to read files only on the server from which it was downloaded.

Fig. 19.3. Reading a file by opening a connection through a URL.

Image

Image

Image

Fig. 19.4. Test class for ReadServerFile.

Image

Image

The application class ReadServerFile contains JTextField enterField, in which the user enters the URL of the file to read and JEditorPane contentsArea to display the contents of the file. When the user presses the Enter key in enterField, the application calls method actionPerformed (lines 31–34). Line 33 uses ActionEvent method getActionCommand to get the string the user input in the JTextField and passes the string to utility method getThePage (lines 61–74).

Line 65 invokes JEditorPane method setPage to download the document specified by location and display it in the JEditorPane. If there is an error downloading the document, method setPage throws an IOException. Also, if an invalid URL is specified, a MalformedURLException (a subclass of IOException) occurs. If the document loads successfully, line 66 displays the current location in enterField.

Typically, an HTML document contains hyperlinks—text, images or GUI components which, when clicked, provide quick access to another document on the web. If a JEditorPane contains an HTML document 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. Lines 42–53 register a HyperlinkListener to handle HyperlinkEvents. When a HyperlinkEvent occurs, the program calls method hyperlinkUpdate (lines 46–51). Lines 48–49 use HyperlinkEvent method getEventType to determine the type of the HyperlinkEvent. Class HyperlinkEvent contains a public nested class called EventType that declares three static EventType objects, which represent the hyperlink event types. ACTIVATED indicates that the user clicked a hyperlink to change web pages, ENTERED indicates that the user moved the mouse over a hyperlink and EXITED indicates that the user moved the mouse away from a hyperlink. If a hyperlink was ACTIVATED, line 50 uses HyperlinkEvent method getURL to obtain the URL represented by the hyperlink. Method toString converts the returned URL to a string that can be passed to utility method getThePage.

Look-and-Feel Observation 19.1

Image

A JEditorPane generates HyperlinkEvents only if it is uneditable.

19.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 the examples, it was not your 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( portNumberqueueLength );

registers an available TCP port number and specifies the maximum number of clients that can wait to connect to the server (i.e., the queue length). The port number is used by clients to locate the server application on the server computer. This is often called the handshake point. If the queue is full, the server refuses client connections. The constructor 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. Only one application at a time can be bound to a specific port on the server.

Software Engineering Observation 19.1

Image

Port numbers can be between 0 and 65,535. Most operating systems reserve port numbers below 1024 for system services (e.g., 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 bind to port numbers below 1024.

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

Socket connection = server.accept();

which returns a Socket when a connection with a client is established. The Socket allows the server to interact with the client. The interactions with the client actually occur at a different server port from the handshake point. This allows the port specified in Step 1 to be used again in a multithreaded server to accept another client connection. We demonstrate this concept in Section 19.8.

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 and receives information from the client via an InputStream. The server invokes method getOutputStream on the Socket to get a reference to the Socket’s OutputStream and invokes method getInputStream on the Socket to get a reference to the Socket’s InputStream.

The stream objects can be used to send or receive individual bytes or sequences of bytes with the OutputStream’s method write and the InputStream’s method read, respectively. Often it is useful to send or receive values of primitive types (e.g., int and double) or Serializable objects (e.g., Strings or other serializable types) rather than sending bytes. In this case, we can use the techniques discussed in Chapter 14 to wrap other stream types (e.g., ObjectOutputStream and ObjectInputStream) around 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. The transmission of the data over the network is seamless and is handled completely by Java.

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

Software Engineering Observation 19.2

Image

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

Software Engineering Observation 19.3

Image

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

Software Engineering Observation 19.4

Image

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. Alternatively, 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. See Chapter 18 for more information on multithreading.

Performance Tip 19.2

Image

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, it need not incur the overhead of thread creation. When the connection is closed, the thread is returned to the pool for reuse.

19.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( serverAddressport );

uses the Socket constructor with two arguments—the server’s address (serverAddress) and the port number. If the connection attempt is successful, this statement returns a Socket. A connection attempt that fails throws an instance of a subclass of IOException, so many programs simply catch IOException. An UnknownHostException occurs specifically when the system is unable to resolve the server address specified in the call to the Socket constructor to a corresponding IP address.

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, we can use the techniques of Chapter 14 to wrap other stream types around the InputStream and OutputStream associated with the Socket. If the server is sending information in the form of actual 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 streams and on the Socket. The client must determine when the server is finished sending information so that it 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.

19.6 Client/Server Interaction with Stream Socket Connections

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

Fig. 19.5. Server portion of a client/server stream-socket connection.

Image

Image

Image

Image

Image

Image

Image

Server Class

Server’s constructor (lines 30–55) creates the server’s GUI, which contains a JTextField and a JTextArea. Server displays its output in the JTextArea. When the main method (lines 7–12 of Fig. 19.6) executes, it creates a Server object, specifies the window’s default close operation and calls method runServer (declared at lines 58–87).

Fig. 19.6. Test class for Server.

Image

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

Common Programming Error 19.1

Image

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

Line 68 calls method waitForConnection (declared at lines 90–96) to wait for a client connection. After the connection is established, line 69 calls method getStreams (declared at lines 99–109) to obtain references to the streams for the connection. Line 70 calls method processConnection (declared at lines 112–133) to send the initial connection message to the client and to process all messages received from the client. The finally block (lines 76–80) terminates the client connection by calling method closeConnection (lines 136–151) even if an exception occurs. Method displayMessage (lines 169–180) is called from these methods to use the event dispatch thread to display messages in the application’s JTextArea.

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

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

Software Engineering Observation 19.5

Image

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

Performance Tip 19.3

Image

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

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

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

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

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

Client Class

Like class Server, class Client’s (Fig. 19.7) constructor (lines 29–56) creates the GUI of the application (a JTextField and a JTextArea). Client displays its output in the text-area. When method main (lines 7–19 of Fig. 19.8) executes, it creates an instance of class Client, specifies the window’s default close operation and calls method runClient (declared at lines 59–79). In this example, you can execute the client from any computer on the Internet and specify the IP address or host name of the server computer as a command-line argument to the program. For example, the command

java Client 192.168.1.15

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

Fig. 19.7. Client portion of a stream-socket connection between client and server.

Image

Image

Image

Image

Image

Image

Image

Fig. 19.8. Class that tests the Client.

Image

Image

Client method runClient (lines 59–79) sets up the connection to the server, processes messages received from the server and closes the connection when communication is complete. Line 63 calls method connectToServer (declared at lines 82–92) to perform the connection. After connecting, line 64 calls method getStreams (declared at lines 95–105) to obtain references to the Socket’s stream objects. Then line 65 calls method processConnection (declared at lines 108–126) to receive and display messages sent from the server. The finally block (lines 75–78) calls closeConnection (lines 129–144) to close the streams and the Socket even if an exception has occurred. Method displayMessage (lines 162–173) is called from these methods to use the event-dispatch thread to display messages in the application’s textarea.

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

InetAddress.getByName( "localhost" )
InetAddress.getLocalHost()

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

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

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

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

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

19.7 Connectionless Client/Server Interaction with Datagrams

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

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 the call, even when you are not talking.

Connectionless transmission with datagrams is more like the way the postal service carries mail. 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 letter is then mailed at the same time. The letters could arrive in order, out of order or not at all (though 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 need not 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 at the receiving computer.

Figures 19.919.12 use datagrams to send packets of information via the User Datagram Protocol (UDP) between a client application and a server application. In the Client application (Fig. 19.11), the user types a message into a text field 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. 19.9) receives the packet and displays the information in it, then echoes the packet back to the client. Upon receiving the packet, the client displays the information it contains.

Fig. 19.9. Server side of connectionless client/server computing with datagrams.

Image

Image

Image

Image

Fig. 19.10. Class that tests the Server.

Image

Server window after packet of data is received from Client

Image

Fig. 19.11. Client side of connectionless client/server computing with datagrams.

Image

Image

Image

Image

Fig. 19.12. Class that tests the Client.

Image

Client window after sending packet to Server and receiving packet back from Server

Image

Server Class

Class Server (Fig. 19.9) declares two DatagramPackets that the server uses to send and receive information and one DatagramSocket that sends and receives the packets. The Server constructor (lines 19–37) creates the graphical user interface in which the packets of information will be displayed. Line 30 creates the DatagramSocket in a try block. Line 30 uses the DatagramSocket constructor that takes an integer port number argument (5000 in this example) to bind the server to a port where it can receive packets from clients. Clients sending packets to this Server specify the same port number in the packets they send. A SocketException is thrown if the DatagramSocket constructor fails to bind the DatagramSocket to the specified port.

Common Programming Error 19.2

Image

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

Server method waitForPackets (lines 40–68) uses an infinite loop to wait for packets to arrive at the Server. Lines 47–48 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 in which the data will be stored and the length of the array. Line 50 uses DatagramSocket method receive to wait for a packet to arrive at the Server. Method receive blocks until a packet arrives, then stores the packet in its DatagramPacket argument. The method throws an IOException if an error occurs while receiving a packet.

When a packet arrives, lines 53–58 call method displayMessage (declared at lines 86–97) to append the packet’s contents to the textarea. DatagramPacket method getAddress (line 54) returns an InetAddress object containing the host name of the computer from which the packet was sent. Method getPort (line 55) returns an integer specifying the port number through which the host computer sent the packet. Method getLength (line 56) returns an integer representing the number of bytes of data sent. Method getData (line 57) returns a byte array containing the data. Lines 57–58 initialize a String object using a three-argument constructor that takes a byte array, the offset and the length. This String is then appended to the text to display.

After displaying a packet, line 60 calls method sendPacketToClient (declared at lines 71–83) to create a new packet and send it to the client. Lines 77–79 create a DatagramPacket and pass four arguments to its 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 81 sends the packet over the network. Method send of DatagramSocket throws an IOException if an error occurs while sending a packet.

Client Class

Class Client (Fig. 19.11) works similarly to class Server, except that the Client sends packets only when the user types a message in a text field and presses the Enter key. When this occurs, the program calls method actionPerformed (lines 32–57), which converts the string the user entered into a byte array (line 41). Lines 44–45 create a DatagramPacket and initialize it with the byte array, the length of the string that was entered by the user, the IP 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 (5000 in this example). Line 47 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.

Note that the DatagramSocket constructor call (line 71) in this application does not specify any arguments. This no-argument 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 it receives a packet of information.

Client method waitForPackets (lines 81–107) uses an infinite loop to wait for packets from the server. Line 91 blocks until a packet arrives. 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 91 stores it in receivePacket, and lines 94–99 call method displayMessage (declared at lines 110–121) to display the packet’s contents in the textarea.

19.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 (Figs. 19.1319.14) that allows two TicTacToeClient applications (Figs. 19.1519.16) to connect to the server and play Tic-Tac-Toe. Sample outputs are shown in Fig. 19.17.

Fig. 19.13. Server side of the client/server Tic-Tac-Toe program.

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Fig. 19.14. Class that tests the Tic-Tac-Toe server.

Image

Image

Fig. 19.15. Client side of the client/server Tic-Tac-Toe program.

Image

Image

Image

Image

Image

Image

Image

Image

Image

Fig. 19.16. Test class for the Tic-Tac-Toe client.

Image

Fig. 19.17. Sample outputs from the client/server Tic-Tac-Toe program.

Image

TicTacToeServer Class

As the TicTacToeServer receives each client connection, it creates an instance of inner-class Player (lines 182–301 of Fig. 19.13) to process the client in a separate thread. These threads enable the clients to play the game independently. The first client to connect to the server is player X and the second is player O. Player X moves first. The server maintains the board information so it can determine whether a player’s move is valid or invalid.

We begin with a discussion of the server side of the Tic-Tac-Toe game. When the TicTacToeServer application executes, the main method (lines 7–12 of Fig. 19.14) creates a TicTacToeServer object called application. The constructor (lines 34–69 of Fig. 19.13) attempts to set up a ServerSocket. If successful, the program displays the server window, then main invokes the TicTacToeServer method execute (lines 72–100). Method execute loops twice, blocking at line 79 each time while waiting for a client connection. When a client connects, line 79 creates a new Player object to manage the connection as a separate thread, and line 80 executes the Player in the runGame thread pool.

When the TicTacToeServer creates a Player, the Player constructor (lines 192–208) receives the Socket object representing the connection to the client and gets the associated input and output streams. Line 201 creates a Formatter (see Chapter 24) by wrapping it around the output stream of the socket. The Player’s run method (lines 219–297) controls the information that is sent to and received from the client. First, it passes to the client the character that the client will place on the board when a move is made (line 225). Line 226 calls Formatter method flush to force this output to the client. Line 241 suspends player X’s thread as it starts executing, because player X can move only after player O connects.

After player O connects, the game can be played, and the run method begins executing its while statement (lines 264–283). Each iteration of this loop reads an integer (line 269) representing the location where the client wants to place a mark, and line 272 invokes the TicTacToeServer method validateAndMove (declared at lines 118–163) to check the move. If the move is valid, line 275 sends a message to the client to this effect. If not, line 280 sends a message indicating that the move was invalid. 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 validateAndMove (lines 118–163 in class TicTacToeServer) allows only one player at a time to move, thereby preventing them 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), it is placed in a wait state until its 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 (line 142), notifies the other Player object (line 146) that a move has been made (so that the client can be sent a message), invokes method signal (line 152) so that the waiting Player (if there is one) can validate a move and returns true (line 159) to indicate that the move is valid.

TicTacToeClient Class

Each TicTacToeClient application (Fig. 19.15) 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. Inner class Square (lines 205–262 of Fig. 19.15) implements each of the nine squares on the board. When a TicTacToeClient 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 startClient method (lines 80–100) opens a connection to the server and gets the associated input and output streams from the Socket object. Lines 85–86 make a connection to the server. 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 99 executes the client with the worker ExecutorService. The run method (lines 103–124) controls the separate thread of execution. The method first reads the mark character (X or O) from the server (line 105), then loops continuously (lines 121–125) and reads messages from the server (line 124). Each message is passed to the processMessage method (lines 129–156) for processing.

If the message received is "Valid move.", lines 134–135 display the message "Valid move, please wait." and call method setMark (lines 173–184) 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.", line 139 displays the message so that the user can click a different square. If the message received is "Opponent moved.", line 145 reads an integer from the server indicating where the opponent moved, and lines 149–150 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 155 simply displays the message. Figure 19.17 shows sample screen captures of two applications interacting via the TicTacToeServer.

19.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 limited because of security concerns. Many web browsers, such as Mozilla 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’ll 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 make network connections. 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 may seem too strict. However, the Java Security API now provides capabilities for digitally signed applets that will enable browsers to determine whether an applet is downloaded from a trusted source. A trusted applet can be given additional access to the computer on which it is executing.

19.10 [Web Bonus] Case Study: DeitelMessenger Server and Client

[Note: This case study is available at www.deitel.com/books/javafp/.] Chat rooms have become common on the Internet. They provide a central location where users can chat with each other via short text messages. Each participant can see all messages that the other users post, and each user can post messages. This case study integrates many of the Java networking, multithreading and Swing GUI features you have learned thus far to build an online chat system. We also introduce multicasting, which enables an application to send DatagramPackets to groups of clients.

The DeitelMessenger 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 support different network protocols and provide different user interfaces. After reading this case study, you’ll be able to build more significant networking applications.

19.11 Wrap-Up

In this chapter, you have learned the basics of network programming in Java. You learned two different methods of sending data over a network: streams-based networking using TCP/IP and datagrams-based networking using UDP. In the next chapter, you’ll learn basic database concepts, how to interact with data in a database using SQL and how to use JDBC to allow Java applications to manipulate database data.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset