SOCKETS AND THE JAVA.NET API

Một phần của tài liệu sams java distributed objects (Trang 228 - 233)

Sockets let you send and receive messages—called datagrams—over a TCP/IP network.

You can use either of two types of sockets: Internet Protocol (IP) sockets or User Datagram Protocol (UDP) sockets. IP sockets send and receive IP datagrams, whereas UDP sockets send and receive UDP datagrams. IP sockets are the garden-variety socket; UDP sockets are less often used. In this chapter, you’ll learn how to work with both types.

Java’s java.net package supports socket input and output. Because java.net is a core Java package, it’s available to every standard Java virtual machine. The classes and methods of java.net are easy to use, but powerful.

Before beginning our study of sockets, let’s take time to understand their pros and cons.

Even if you find that sockets don’t meet your application’s needs, you should spend the time to understand them thoroughly. All other TCP/IP data exchange facilities are built using sockets, which are the fundamental mechanisms of TCP/IP data transfer. You’ll find a knowledge of sockets helpful in understanding how other data transfer

mechanisms work.

Pros and Cons of Sockets

Sockets are the assembly language of TCP/IP data transfer, whereas other mechanisms, such as Remote Method Invocation (RMI) and Common Object Request Broker

Architecture (CORBA), are the high-level languages. Just as assembly language programs—in principle—run faster and use memory more efficiently than high-level language programs, sockets offer better performance than other transfer mechanisms.

However, beware the word offer, because what is offered is not always achieved. Some assembly language programs are just as slow and fat as high-level language programs.

Similarly, some applications that use sockets function less efficiently than some applications that use higher-level data transfer mechanisms.

If you can’t be certain of the benefits of assembly language and sockets, you can be relatively certain of the cost. An assembly language program is almost always more difficult and expensive to write than an equivalent high-level language program. That’s why assembly language programs have become as rare as Studebakers (a popular car during the 1940s). Similarly, if the data you want to transfer is simple—a line of ASCII text, for instance—sockets will serve you well. However, if you need to transfer an entire community of objects, you can expect to spend a great deal of time at the keyboard.

Moreover, because your application will be large and complex, you can expect it to

contain a relatively large number of troublesome bugs.

Sockets model network data transfers as streams of bytes. When your data is simple, sockets are an ideal choice. But when your data is complex, you should consider other options, such as RMI or CORBA. However, RMI, CORBA, and other high-level protocols do not transfer data as efficiently as sockets. When the volume of data is small, this inefficiency poses no problem. However, when an application must transfer large

volumes of data, the choice of data transfer protocol is complicated. You can aim for high efficiency, trading off the increased development and maintenance effort associated with socket programming for potentially improved performance, or you can use a higher-level protocol and hope that the efficiency is acceptable.

Several factors should guide your choice. First, consider your training and skills and those of other development staff members. If you’re not familiar with socket

programming, for example, writing an application that uses sockets will pose special risks. Second, consider the possibility of improving system performance by means of more powerful hardware. If your application is very large, you may not have this option.

Often, though, a more powerful server or a faster network can compensate for some programming shortcuts that, in the end, offer better value.

When neither of these considerations leads to a clear decision, we suggest you use a high-level protocol such as RMI or CORBA rather than sockets. By using a high-level protocol, you operate on a higher level of abstraction—one closer to the users’ problems than to the technology applied to solve it. You’re therefore more likely to build a correct application—one that actually solves the users’ problems. Socket programming may confront you with so many technology-oriented choices that you inadvertently lose sight of the users’ problems: Many projects have taken this sad path.

Once you’ve built a working system, you can assess its performance. You might find that it performs acceptably, in which case you’ve made a winning choice. Of course, you might find that performance is unacceptable. In that case, you can replace some or all of the data transfers with socket-based interaction. The work spent to implement data transfer using high-level protocols is not wasted, however. It will act as a functional specification that guides your implementation of socket-based data transfer. As a result, you’re much more likely to implement a reliable data transfer than if you had originally programmed the application to use sockets.

Now that you know when to apply sockets and when to choose another data transfer mechanism, let’s begin our study of sockets. We’ll first examine four java.net classes that you’ll often use in network data transfers: URL , InetAddress, Socket, and ServerSocket.

The URL Class

If your application needs to transfer data in only one direction, you may prefer to use the HTTP protocol rather than the IP or UDP protocol. The HTTP protocol, the protocol used by Web clients and servers, allows a client to download information from a Web server.

The client sends the server a Uniform Resource Locator (URL) that specifies the desired document, and the server responds by transmitting the document. The URL class encapsulates a URL, letting you easily contact a server and download a document. You can create a URL by using the following constructor:

public URL(String url) throws MalformedURLException

The String argument specifies the document that the URL refers to. For example, the Macmillan Computer Publishing Web site is http://www.mcp.com . You can create a URL that refers to the Macmillan Web site by writing this:

URL macmillan = new URL("http://www.mcp.com");

The constructor throws an exception if the format of its argument does not represent a valid URL. Therefore, you must enclose the statement in a try-catch block or specify that the method that encloses the statement should throw the

MalformedURLException (or one of its ancestor classes, such as Exception).

Once you’ve constructed a URL that points to a Web document, you can fetch the document by using the openStream method, which returns an InputStream containing the document contents. You simply read the InputStream to access the document contents. Some types of documents can be read more easily: The

getContent method returns an Object that encapsulates the document contents.

However, getContent succeeds only if the Java virtual machine contains an appropriate content handler for the type of document accessed.

Table 13.1 summarizes key methods and constructors of the URL class. You’ll learn more about URLs in Chapter 18, “Servlets and Common Gateway Interface (CGI),” which describes the Java servlet API.

TABLE 13.1 KEY METHODS OF THE URL CLASS

Method Return Value

boolean equals(Object x) Returns true if the URL refers to the same document as the specified object, x, which should be a URL.

Object getContent() Returns an object that encapsulates the document that the URL refers to. This method fails if the type of document content cannot be handled.

String getFile() Returns the file portion of the URL.

String getHost() Returns the host portion of the URL.

int getPort() Returns the port portion of the URL.

String

getProtocol() Returns the protocol portion of the URL.

getRef() Returns the reference portion (the HTML name tag) of the URL.

InputStream

openStream() Returns an InputStream that encapsulates the document content.

The InetAddress Class

The InetAddress class represents the address of an Internet host. You use

InetAddress to construct Socket and DatagramPacket objects, which you’ll meet in later sections of this chapter. The InetAddress class has no constructor; instead, it provides three static methods that each return an instance of InetAddress:

• InetAddress getLocalHost() , which returns an InetAddress that represents the local host (the one running the Java program).

• InetAddress getByName(String host ), which returns an InetAddress of the specified host, if the host’s address can be determined. For example,

getByName("www.mcp.com")

returns the Internet address of the Macmillan Web server.

• InetAddress [] getAllByName(String host ), which returns an array containing all the Internet addresses of the specified host. A host has multiple addresses if it has multiple network interfaces, because each interface has a unique address.

Each of these methods throws UnknownHostException if it’s unable to determine the Internet address of the host.

The Socket Class

An Internet host has a set of numbered ports that it can use to communicate with other hosts. The Socket class encapsulates a port, providing methods that let you use the port to send and receive data. You can create a Socket by using the constructor Socket(String host , int port )

or

Socket(InetAddress address, int port )

For example, to open a socket that sends to port 80 of the host www.mcp.com, you write this:

Socket mcp = new Socket("www.mcp.com", 80;)

The Socket class also provides more specialized constructors, but you’ll seldom use them. The Socket constructors throw exceptions if they fail; your programs need to include a try-catch block or a throws clause to handle them.

Once you’ve created a socket, you can use the getInputStream method to get an associated InputStream and getOutputStream to get an associated

OutputStream. By using ordinary input/output methods to read and write these streams, you can perform socket input and output as simply as file input and output.

If you’ve worked with InputStream and OutputStream, you’ve probably discovered that their cousins— BufferedInputStream and PrintStream—are more convenient to use.

You could wrap the InputStream returned by getInputStream in a

BufferedInputStream and the OutputStream in a PrintStream, but this quickly grows tiresome. An easier, more object-oriented approach is to wrap the Socket in a new class that provides the appropriate stream wrappers. Listing 13.1 shows the

BetterSocket class, which does just that. The BetterSocket class does not extend the Socket class; instead, it includes a field that references a Socket. This makes it possible to construct a BetterSocket from a Socket. The BetterSocket class

does not offer the full range of methods provided by the Socket class: You could revise BetterSocket to define each of them, giving each method a body that forwards the corresponding message to the Socket instance and returns the result. However, the Socket class has many methods; therefore, defining corresponding BetterSocket methods would be tedious. Instead, the BetterSocket class provides an accessor method, getSocket, that returns a reference to the Socket instance. You can accomplish any desired Socket operation by getting a reference to the Socket instance and sending the Socket the appropriate message.

In addition to wrapping the Socket streams in a BufferedInputFile and PrintStream, BetterSocket provides readLine and println methods that make it easy to send lines of text over the Socket data streams. You’ll see how this works in an upcoming sample program. Notice also the close method that releases a Socket that’s no longer needed.

LISTING 13.1 BetterSocket.java —AN IMPROVED SOCKET CLASS

import java.net.*;

import java.io.*;

public class BetterSocket {

Socket theSocket;

BufferedReader theReader;

PrintWriter theWriter;

public BetterSocket(String host, int port) throws UnknownHostException, IOException {

this(new Socket(host, port));

}

public BetterSocket(Socket s)

throws UnknownHostException, IOException {

theSocket = s;

theReader = new BufferedReader(

new InputStreamReader(

theSocket.getInputStream()));

theWriter = new PrintWriter(

theSocket.getOutputStream(), true);

}

public Socket getSocket() {

return theSocket;

}

public void close() throws IOException {

theSocket.close();

}

public String readLine() throws IOException {

return theReader.readLine();

}

public void println(String s) throws IOException

{

theWriter.println(s);

} }

The ServerSocket Class

The last of the classes we’ll study before looking at a sample program is

ServerSocket. The ServerSocket class, as you may have guessed, is used by server programs. It has a method, accept, that lets a program wait for a request sent by a client and then returns a Socket that the client and server can use to exchange data.

Obligingly, the Socket object on the client side automatically switches to communicate using the new Socket. Figure 13.1 illustrates the operation of a ServerSocket. To create a ServerSocket, you use the following constructor:

ServerSocket(int port )

This constructor throws an IOException if it fails; otherwise, your ServerSocket is ready for use. When you send the ServerSocket the accept message, it blocks until a client request arrives and then returns a Socket. For example, to create a

ServerSocket that listens on port 1234 for a client request, you could write:

Figure 13.1: The ServerSocket returns a Socket that’s used to exchange data between a client and server.

ServerSocket server = new ServerSocket(1234);

Socket client = server.accept();

You’ll soon meet some additional ServerSocket methods, but let’s press on to study our first sample program.

Một phần của tài liệu sams java distributed objects (Trang 228 - 233)

Tải bản đầy đủ (PDF)

(693 trang)