© Peter Späth and Jeff Friesen 2020
P. Späth, J. FriesenLearn Java for Android Developmenthttps://doi.org/10.1007/978-1-4842-5943-6_13

13. Accessing Networks

Peter Späth1  and Jeff Friesen2
(1)
Leipzig, Sachsen, Germany
(2)
Winnipeg, MB, Canada
 

Applications often need to access networks to acquire resources (such as images) or to communicate with remote executable entities (such as web services). A network is a group of interconnected nodes (computing devices such as tablets, and peripherals such as scanners or laser printers) that can be shared among the network’s users.

Note

Intranets (networks within an organization) and internets often use TCP/IP (http://en.wikipedia.org/wiki/TCP/IP_model) to communicate between nodes. TCP/IP includes Transmission Control Protocol (TCP) , which is a connection-oriented protocol; User Datagram Protocol (UDP) , which is a connectionless protocol; and Internet Protocol (IP) , which is the basic protocol over which TCP and UDP perform their tasks.

The java.net package provides types that support TCP/IP between processes (executing applications) running on the same or different hosts (computer-based TCP/IP nodes). In this chapter, we first present the types for performing socket-based and URL-based communication. We then present the low-level network interface and interface address types and cookie-oriented types.

Accessing Networks via Sockets

Two processes communicate by way of sockets, which are endpoints in a communications link between these processes. Each endpoint is identified by an IP address that identifies the host and by a port number that identifies the process running on that host.

Network nodes are identified via IP addresses, and a port number specifying a communication channel identifier.

One process writes a message (a sequence of bytes) to its socket. The network management software portion of the underlying platform breaks the message into a sequence of packets (addressable message chunks that are often referred to as IP datagrams) and forwards them to the other process’s socket where they are recombined into the original message for processing.

Figure 13-1 shows how two sockets communicate in a TCP/IP context.
../images/323065_4_En_13_Chapter/323065_4_En_13_Fig1_HTML.png
Figure 13-1

Two processes communicate via a pair of sockets

In the context of Figure 13-1, suppose that Process A wants to send a message to Process B. Process A sends that message to its socket with the destination socket address of Process B. Host A’s network management software (often referred to as a protocol stack ) obtains this message and reduces it to a sequence of packets, with each packet including the destination host’s IP address and port number. The network management software then sends these packets through Host A’s network interface card (NIC) to Host B.

Note

The NIC’s various network interfaces are connections between a computer and a network.

Host B’s protocol stack receives packets through the NIC and reassembles them into the original message (packets may be received out of order), which it then makes available to Process B via its socket. This scenario reverses when Process B communicates with Process A.

The network management software uses TCP to create an ongoing conversation between two hosts in which messages are sent back and forth. Before this conversation occurs, a connection is established between these hosts. After the connection has been established, TCP enters a pattern where it sends message packets and waits for a reply that they arrived correctly (or for a timeout to expire when the reply doesn’t arrive because of some network problem). This pattern repeats and guarantees a reliable connection. For detailed information on this pattern, check out http://en.wikipedia.org/wiki/Tcp_receive_window#Flow_control.

Because it can take time to establish a connection, and it also takes time to send packets (as it is necessary to receive reply acknowledgments and also because of timeouts), TCP is slow. On the other hand, UDP, which doesn’t require connections and packet acknowledgment, is much faster. The downside is that UDP isn’t as reliable (there’s no guarantee of packet delivery, ordering, or protection against duplicate packets, although UDP uses checksums to verify that data is correct) because there’s no acknowledgment. Furthermore, UDP is limited to single-packet conversations.

The java.net package provides Socket, ServerSocket, and other Socket-suffixed classes for performing TCP-based or UDP-based communications. Before investigating these classes, you need to understand socket addresses and socket options.

Socket Addresses

An instance of a Socket-suffixed class is associated with a socket address comprised of an IP address and a port number. These classes often rely on the InetAddress class to represent the IPv4 or IPv6 address portion of the socket address, and represent the port number separately.

Note

InetAddress relies on its Inet4Address subclass to represent an IPv4 address and on its Inet6Address subclass to represent an IPv6 address.

InetAddress declares several class methods for obtaining an InetAddress instance. For details please consult the API documentation for class InetAddress.

The abstract class SocketAddress represents a socket address “with no protocol attachment.” (This class’s creator might have anticipated that Java would eventually support low-level communication protocols other than the widely popular Internet Protocol.)

SocketAddress is subclassed by the concrete InetSocketAddress class, which represents a socket address as an IP address and a port number. It can also represent a hostname and a port number and will make an attempt to resolve the hostname.

InetSocketAddress instances are created by invoking InetSocketAddress(InetAddress addr, int port) and other constructors. After an instance has been created, you can call methods such as InetAddress getAddress() and int getPort() to return socket address components.

Socket Options

An instance of a Socket-suffixed class shares the concept of socket options, which are parameters for configuring socket behavior. Socket options are described by constants that are declared in the SocketOptions interface.
  • IP_MULTICAST_IF specifies the outgoing network interface for multicast packets (on multihomed [multiple NIC] hosts). This option isn’t implemented by Android.

  • IP_MULTICAST_IF2 specifies the outgoing network interface for multicast packets using an interface index.

  • IP_MULTICAST_LOOP enables or disables local loopback of multicast datagrams.

  • IP_TOS sets the type-of-service (IPv4) or traffic class (IPv6) field in the IP header for a TCP or UDP socket.

  • SO_BINDADDR fetches the socket’s local address binding. This option isn’t implemented by Android.

  • SO_BROADCAST enables a socket to send broadcast messages.

  • SO_KEEPALIVE turns on socket keepalive.

  • SO_LINGER specifies the number of seconds to wait when closing a socket when there is still some buffered data to be sent.

  • SO_OOBINLINE enables inline reception of TCP urgent data.

  • SO_RCVBUF sets or gets the maximum socket receive buffer size (in bytes).

  • SO_REUSEADDR enables a socket’s reuse address.

  • SO_SNDBUF sets or gets the maximum socket send buffer size (in bytes).

  • SO_TIMEOUT specifies a timeout (in milliseconds) on blocking accept or read/receive (but not write/send) socket operations. (Don’t block forever!)

  • TCP_NODELAY disables Nagle’s algorithm (http://en.wikipedia.org/wiki/Nagle's_algorithm). Written data to the network is not buffered pending acknowledgment of previously written data.

SocketOptions also declares the following methods for setting and getting these options:
  • void setOption(int optID, Object value)

  • Object getOption(int optID)

optID is one of the aforementioned constants and value is an object of a suitable class (such as java.lang.Boolean).

SocketOptions is implemented by the abstract SocketImpl and DatagramSocketImpl classes. Concrete instances of these classes are wrapped by the various Socket-suffixed classes. As a result, you cannot invoke these methods. Instead, you work with the typesafe setter and getter methods provided by the Socket-suffixed classes for setting and getting these options.

For example, Socket declares void setKeepAlive(boolean keepAlive) for setting the SO_KEEPALIVE option, and ServerSocket declares void setSoTimeout(int timeout) for setting the SO_TIMEOUT option. Check the documentation on the Socket-suffixed classes to learn about these and other socket option methods.

Note

Socket option methods that apply to DatagramSocket also apply to its MulticastSocket subclass.

Socket and ServerSocket

The Socket and ServerSocket classes support TCP-based communications between client processes (such as an application running on a tablet) and server processes (such as an application running on one of your Internet service provider’s computers that provides access to the World Wide Web). Because Socket is associated with the java.io.InputStream and java.io.OutputStream classes, sockets based on the Socket class are commonly referred to as stream sockets.

Socket supports the creation of client-side sockets. It declares several constructors for this purpose; see the API documentation.

After a Socket instance is created, it’s bound to an arbitrary local host socket address before a connection is made to the remote host socket address. Binding makes a client socket address available to a server socket so that a server process can communicate with the client process via the server socket.

After creating a Socket instance, and possibly invoking bind() and connect() on that instance, an application invokes Socket’s InputStream getInputStream() and OutputStream getOutputStream() methods to acquire an input stream for reading bytes from the socket and an output stream for writing bytes to the socket. Also, the application often calls Socket’s void close() method to close the socket when no longer needed for I/O.

The following example demonstrates how to create a socket that’s bound to port number 9999 on the local host and then access its input and output streams—exceptions are ignored for brevity:
Socket socket = new Socket("localhost", 9999);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// Do some work with the socket.
socket.close();

ServerSocket supports the creation of server-side sockets. After a server socket is created, a server application enters a loop that first invokes ServerSocket’s Socket accept() method to listen for a connection request and return a Socket instance that lets it communicate with the associated client socket. It then communicates with the client socket to perform some kind of processing. When processing finishes, the server socket calls the client socket’s close() method to terminate its connection with the client.

Note

ServerSocket declares a void close() method for closing a server socket before terminating the server application. An unclosed socket is automatically closed when an application terminates.

The following example demonstrates how to create a server socket that’s bound to port 9999 on the current host, listen for incoming connection requests, return their sockets, perform work on those sockets, and close the sockets—exceptions are ignored for brevity:
ServerSocket ss = new ServerSocket(9999);
while (true) {
   Socket socket = ss.accept();
   // obtain socket input/output streams and communicate with socket
   socket.close();
}

The accept() method call blocks until a connection request is available and then returns a Socket object so that the server application can communicate with its associated client. The socket is closed after this communication takes place. The server socket is automatically closed when the application exits.

This example assumes that socket communication takes place on the server application’s main thread, which is a problem when processing takes time to perform because server response time to incoming connection requests decreases. To speed up response time, it’s often necessary to communicate with the socket on a worker thread, as demonstrated in the following example:
ServerSocket ss = new ServerSocket(9999);
while (true) {
   final Socket s = ss.accept();
   new Thread(new Runnable() {
         @Override
         public void run() {
            // obtain socket input/output streams and communicate with socket
            try { s.close(); } catch (IOException ioe) {}
         }
      }).start();
}

Each time a connection request arrives, accept() returns a Socket instance, and then a java.lang.Thread object is created whose runnable accesses that socket for communicating with the socket on a worker thread.

Tip

Although this example uses the Thread class, you could use an executor (see Chapter 11) instead.

We’ve created EchoClient and EchoServer applications that demonstrate Socket and ServerSocket. Listing 13-1 presents EchoClient’s source code.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class EchoClient {
   public static void main(String[] args) {
      if (args.length != 1) {
         System.err.println("usage  : java EchoClient message");
         System.err.println("example: java EchoClient "This is a test."");
         return;
      }
      try {
         Socket socket = new Socket("localhost", 9999);
         OutputStream os = socket.getOutputStream();
         OutputStreamWriter osw = new OutputStreamWriter(os);
         PrintWriter pw = new PrintWriter(osw);
         pw.println(args[0]);
         pw.flush();
         InputStream is = socket.getInputStream();
         InputStreamReader isr = new InputStreamReader(is);
         BufferedReader br = new BufferedReader(isr);
         System.out.println(br.readLine());
      } catch (UnknownHostException uhe) {
         System.err.println("unknown host: " + uhe.getMessage());
      } catch (IOException ioe) {
         System.err.println("I/O error: " + ioe.getMessage());
      }
   }
}
Listing 13-1

Echoing Data to and Receiving It Back from a Server

EchoClient first verifies that it has received a single command-line argument and then creates a socket that will connect to a process running on port 9999 of the local host.

After creating the socket, EchoClient obtains an output stream for writing a string to the socket. Because the output stream can only handle a sequence of bytes, the java.io.OutputStreamWriter and java.io.PrintWriter classes (see Chapter 12) are used to connect the writer that outputs characters to the byte-oriented output stream.

After instantiating PrintWriter, EchoClient invokes its void println(String str) method to write the string followed by a newline character. The void flush() method is subsequently called to ensure that all pending data is written to the server.

EchoClient now obtains an input stream for reading the string as a sequence of bytes. It then connects the reader (that inputs characters) to the byte-oriented input stream by instantiating java.io.InputStreamReader and java.io.BufferedReader (see Chapter 12).

Finally, EchoClient invokes BufferedReader’s String readLine() method to read the characters followed by a newline from the socket. (readLine() doesn’t include the newline character in the returned string.) These characters followed by a newline are then written to standard output.

Note

In a long-running application, you would explicitly close the socket instance by invoking its void close() method when the socket is no longer needed. For brevity, we’ve chosen not to do so in this and most of the remaining Socket-suffixed class examples.

Listing 13-2 presents EchoServer’s source code.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
   public static void main(String[] args) throws IOException {
      System.out.println("Starting echo server...");
      ServerSocket ss = new ServerSocket(9999);
      while (true) {
         Socket s = ss.accept();
         try {
            InputStream is = s.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String msg = br.readLine();
            System.out.println(msg);
            OutputStream os = s.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os);
            PrintWriter pw = new PrintWriter(osw);
            pw.println(msg);
            pw.flush();
         } catch (IOException ioe) {
            System.err.println("I/O error: " + ioe.getMessage());
         } finally {
            try {
               s.close();
            } catch (IOException ioe) {
            }
         }
      }
   }
}
Listing 13-2

Receiving Data from and Echoing It Back to a Client

EchoServer first outputs an introductory message to standard output and then creates a server socket that listens for connections on port 9999. It then enters an infinite loop, where each iteration invokes ServerSocket’s Socket accept() method to block until a connection is received and then returns a Socket object representing this connection.

After obtaining the socket, EchoServer obtains an input stream for reading from the socket. Because the input stream can only handle a sequence of bytes, the InputStreamReader and BufferedReader classes are used to connect the reader that inputs characters to the byte-oriented input stream.

EchoServer now obtains an output stream for writing the string as a sequence of bytes. It then connects the writer that outputs characters to the byte-oriented output stream by instantiating OutputStreamWriter and PrintWriter.

After outputting the message to standard output, EchoServer calls flush() to flush the output to the client. The client socket is then closed.

To experiment with these applications, copy EchoClient.java and EchoServer.java to the same directory and open two console windows with this directory being current. Compile each source file and execute java EchoServer in one window—you should observe an introductory message, although you might first need to enable port 9999 in case you have a firewall running (http://en.wikipedia.org/wiki/Firewall_(computing)). Having started the server, echo the following command to echo text to both windows:
java EchoClient "This is a test."

You should observe “This is a test.” in both windows.

DatagramSocket and MulticastSocket

The DatagramSocket and MulticastSocket classes let you perform UDP-based communications between a pair of hosts (DatagramSocket) or between many hosts (MulticastSocket). With either class, you communicate one-way messages via datagram packets, which are arrays of bytes associated with instances of the DatagramPacket class .

Note

Although you might think that Socket and ServerSocket are all that you need, DatagramSocket and its MulticastSocket subclass have their uses. For example, consider a scenario in which a group of machines need to occasionally tell a server that they’re alive. It shouldn’t matter when the occasional message is lost or even when the message doesn’t arrive on time. Another example is a low-priority stock ticker that periodically broadcasts stock prices. When a packet doesn’t arrive, odds are that the next packet will arrive and you’ll then receive notification of the latest prices. Timely rather than reliable or orderly delivery is more important in real-time applications.

DatagramPacket declares several constructors with DatagramPacket(byte[] buf, int length) being the simplest. This constructor requires you to pass byte array and integer arguments to buf and length, where buf is a data buffer that stores data to be sent or received and length (which must be less than or equal to buf.length) specifies the number of bytes (starting at buf[0]) to send/receive.

The following example demonstrates this constructor:
byte[] buffer = new byte[100];
DatagramPacket dgp = new DatagramPacket(buffer, buffer.length);
Note

Additional constructors let you specify an offset into buf that identifies the storage location of the first outgoing or incoming byte, and/or let you specify a destination socket address.

DatagramSocket describes a socket for the client or server side of the UDP-communication link. Although this class declares several constructors, we find it convenient in this chapter to use the DatagramSocket() constructor for the client side and the DatagramSocket(int port) constructor for the server side. Either constructor throws SocketException when it cannot create the datagram socket or bind the datagram socket to a local port.

After an application instantiates DatagramSocket, it calls void send(DatagramPacket dgp) and void receive(DatagramPacket dgp) to send and receive datagram packets.

Listing 13-3 demonstrates DatagramPacket and DatagramSocket in a server context.
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class DGServer{
   final static int PORT = 10000;
   public static void main(String[] args) throws SocketException{
      System.out.println("Server is starting");
      DatagramSocket dgs = new DatagramSocket(PORT);
      try {
         System.out.println("Send buffer size = " + dgs.getSendBufferSize());
         System.out.println("Receive buffer size = " +
                            dgs.getReceiveBufferSize());
         byte[] data = new byte[100];
         DatagramPacket dgp = new DatagramPacket(data, data.length);
         while (true) {
            dgs.receive(dgp);
            System.out.println(new String(data));
            dgs.send(dgp);
         }
      } catch (IOException ioe) {
         System.err.println("I/O error: " + ioe.getMessage());
      }
   }
}
Listing 13-3

Receiving Datagram Packets from and Echoing Them Back to Clients

Listing 13-3’s main() method first creates a DatagramSocket object and binds the socket to port 10000 on the local host. It then invokes DatagramSocket’s int getSendBufferSize() and int getReceiveBufferSize() methods to get the values of the SO_SNDBUF and SO_RCVBUF socket options, which are then output.

Note

Sockets are associated with underlying platform send and receive buffers, and their sizes are accessed by calling getSendBufferSize() and getReceiveBufferSize(). Similarly, their sizes can be set by calling DatagramSocket’s void setReceiveBufferSize(int size) and void setSendBufferSize(int size) methods. Although you can adjust these buffer sizes to improve performance, there’s a practical limit with regard to UDP. The maximum size of a UDP packet that can be sent or received is 65,507 bytes under IPv4—it’s derived from subtracting the 8-byte UDP header and 20-byte IP header values from 65,535. Although you can specify a send/receive buffer with a greater value, doing so is wasteful because the largest packet is restricted to 65,507 bytes. Also, attempting to send or receive a packet with a buffer length that exceeds 65,507 bytes results in IOException.

main() next instantiates DatagramPacket in preparation for receiving a datagram packet from a client and then echoing the packet back to the client. It assumes that packets will be 100 bytes or less in size.

Finally, main() enters an infinite loop that receives a packet, outputs packet content, and sends the packet back to the client—the client’s addressing information is stored in DatagramPacket.

Compile Listing 13-3 (javac DGServer.java) and run the application (java DGServer). You should observe output that’s the same as or similar to that shown here:
Server is starting
Send buffer size = 8192
Receive buffer size = 8192
Listing 13-4 demonstrates DatagramPacket and DatagramSocket in a client context.
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class DGClient {
   final static int PORT = 10000;
   final static String ADDR = "localhost";
   public static void main(String[] args) throws SocketException {
      System.out.println("client is starting");
      DatagramSocket dgs = new DatagramSocket();
      try {
         byte[] buffer;
         buffer = "Send me a datagram".getBytes();
         InetAddress ia = InetAddress.getByName(ADDR);
         DatagramPacket dgp = new DatagramPacket(buffer, buffer.length, ia, PORT);
         dgs.send(dgp);
         byte[] buffer2 = new byte[100];
         dgp = new DatagramPacket(buffer2, buffer.length, ia, PORT);
         dgs.receive(dgp);
         System.out.println(new String(dgp.getData()));
      } catch (IOException ioe) {
         System.err.println("I/O error: " + ioe.getMessage());
      }
   }
}
Listing 13-4

Sending a Datagram Packet to and Receiving It Back from a Server

Listing 13-4 is similar to Listing 13-3, but there’s one big difference. We use the DatagramPacket(byte[] buf, int length, InetAddress address, int port) constructor to specify the server’s destination, which happens to be port 10000 on the local host, in the datagram packet. The send() method call routes the packet to this destination.

Compile Listing 13-4 (javac DGClient.java) and run the application (java DGClient). Assuming that DGServer is also running, you should observe the following output in DGClient’s command window (and the last line of this output in DGServer’s command window):
client is starting
Send me a datagram

MulticastSocket describes a socket for the client or server side of a UDP-based multicasting session. Two commonly used constructors are MulticastSocket() (it creates a multicast socket not bound to a port) and MulticastSocket(int port) (it creates a multicast socket bound to the specified port). Either constructor throws IOException when an I/O error occurs.

What is Multicasting?

Previous examples have demonstrated unicasting, which occurs when a server sends a message to a single client. However, it’s also possible to broadcast the same message to multiple clients (such as transmit a “school closed due to bad weather” announcement to all members of a group of parents who have registered with an online program to receive this announcement); this activity is known as multicasting.

A server multicasts by sending a sequence of datagram packets to a special IP address, which is known as a multicast group address , and a specific port (as specified by a port number). Clients wanting to receive those datagram packets create a multicast socket that uses that port number. They request to join the group through a join group operation that specifies the special IP address. At this point, the client can receive datagram packets sent to the group and can even send datagram packets to other group members. After the client has read all datagram packets that it wants to read, it removes itself from the group by applying a leave group operation that specifies the special IP address.

IPv4 addresses 224.0.0.1 to 239.255.255.255 (inclusive) are reserved for use as multicast group addresses.

Accessing Networks via URLs

A Uniform Resource Locator (URL) is a character string that specifies where a resource (such as a web page) is located on a TCP-/IP-based network (such as the Internet). Also, it provides the means to retrieve that resource. For example, http://some.domain.tld is a URL that locates some website’s main page. The http:// prefix specifies that HyperText Transfer Protocol (HTTP) , which is a high-level protocol on top of TCP/IP for locating HTTP resources (such as web pages), must be used to retrieve the web page located at some.domain.tld.

URN, URL, and URI

A Uniform Resource Name (URN) is a character string that names a resource and doesn’t provide a way to access that resource (the resource might not be available). For example, urn:isbn:9781430264545 identifies an Apress book named Learn Java for Android Development and that’s all.

URNs and URLs are examples of Uniform Resource Identifiers (URIs) , which are character strings for identifying names (URNs) and resources (URLs). Every URN and URL is also a URI.

The java.net package provides URL and URLConnection classes for accessing URL-based resources. It also provides URLEncoder and URLDecoder classes for encoding and decoding URLs as well as the URI class for performing URI-based operations (such as relativization) and returning URL instances containing the results.

URL and URLConnection

The URL class represents URLs and provides access to the resources to which they refer. Each URL instance unambiguously identifies an Internet resource.

URL declares several constructors, with URL(String s) being the simplest. This constructor creates a URL instance from the String argument passed to s and is demonstrated as follows:
URL url = null;
try {
   url = new URL("http://example.com");
} catch (MalformedURLException murle) {
   // handle the exception
}

This example creates a URL object that uses HTTP to access the web page at http://example.com. If we specified an illegal URL (such as foo), the constructor would throw MalformedURLException (an IOException subclass).

Although you’ll commonly specify http:// as the protocol prefix, this isn’t your only choice. For example, you can also specify file:/// when the resource is located on the local host. Furthermore, you can prepend jar: to either http:// or file:/// when the resource is stored in a JAR file, as demonstrated here:
jar:file:///C:/path/to/some.jar!//some/package/SomeClass.class

The jar: prefix indicates that you want to access a JAR file resource (such as a stored class file). The file:/// prefix identifies the local host’s resource location.

The path to the JAR file is followed by an exclamation mark (!) to separate the JAR file path from the JAR resource path, which happens to be the /some/package/SomeClass class file entry in this JAR file (the leading / character is required).

Note

The URL class in Oracle’s Java reference implementation supports additional protocols, including ftp.

After creating a URL object, you can invoke various URL methods to access portions of the URL. For example, String getProtocol() returns the protocol portion of the URL (such as http). You can also retrieve the resource by calling the InputStream openStream() method.

openStream() creates a connection to the resource and returns an InputStream instance for reading resource data from that connection, as demonstrated here:
InputStream is = url.openStream();
int ch;
while ((ch = is.read()) != -1)
   System.out.print((char) ch);
Note

For an HTTP connection, an internal socket is created that connects to HTTP port 80 on the server identified via the URL’s domain name/IP address, unless you append a different port number to the domain name/IP address (such as http://example.com:8080).

We’ve created a ListResource application that demonstrates URL by using this class to fetch a resource and list its contents. Listing 13-5 presents ListResource’s source code.
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class ListResource {
   public static void main(String[] args) {
      if (args.length != 1) {
         System.err.println("usage: java ListResource url");
         return;
      } try {
         URL url = new URL(args[0]);
         InputStream is = url.openStream();
         try {
            int ch;
            while ((ch = is.read()) != -1)
               System.out.print((char) ch);
         } finally {
            is.close();
         }
      } catch (MalformedURLException murle) {
         System.err.println("invalid URL");
      } catch (IOException ioe) {
         System.err.println("I/O error: " + ioe.getMessage());
      }
   }
}
Listing 13-5

Listing the Contents of the Resource Identified via a URL Command-Line Argument

ListResource first verifies that it has received a single command-line argument and then attempts to instantiate URL with this argument. Assuming that the URL is valid, which means that MalformedURLException isn’t thrown, ListResource calls openStream() on the URL instance and proceeds to list the resource contents to standard output.

Compile this source code (javac ListResource.java) and execute the following command to list the contents of the page at http://example.com:
java ListResource http://example.com

openStream() is a convenience method for invoking openConnection().getInputStream(). Each of URL’s URLConnection openConnection() and URLConnection openConnection(Proxy proxy) methods returns an instance of the URLConnection class, which represents a communications link between the application and a URL.

URLConnection gives you additional control over client/server communication. For example, you can use this class to output content to various resources that accept content. In contrast, URL only lets you input content via openStream().

The following example shows you how to obtain a URLConnection object from the URL object referenced by the precreated url variable, enable its dooutput property , and obtain an output stream for writing to the resource:
URLConnection urlc = url.openConnection();
urlc.setDoOutput(true);
OutputStream os = urlc.getOutputStream();

URLConnection is subclassed by HttpURLConnection and JarURLConnection. These classes declare constants and/or methods that are specific to working with the HTTP protocol or interacting with JAR-based resources.

For example, HttpURLConnection declares void setRequestMethod(String method) for specifying the HTTP request command to be sent to a remote HTTP server. GET and POST are commonly specified commands.

URLEncoder and URLDecoder

HyperText Markup Language (HTML) lets you introduce forms into web pages that solicit information from page visitors. After filling out a form’s fields, the visitor clicks the form’s Submit button (which may specify something other than Submit), and the form content (field names and values) is sent to a server program. Before sending the form content, a web browser encodes this data by replacing spaces and other URL-illegal characters and sets the content’s Internet media type (also known as Multipurpose Internet Mail Extensions [MIME] type) to application/x-www-form-urlencoded.

Note

The data is encoded for HTTP POST and HTTP GET operations. Unlike POST, GET requires a query string (a ?-prefixed string containing the encoded content) to be appended to the server program’s URL.

The java.net package provides URLEncoder and URLDecoder classes to assist you with the tasks of encoding and decoding form content.

URLEncoder applies the following encoding rules:

  • Alphanumeric characters (a–z, A–Z, and 0–9) remain the same.

  • Special characters “.”, “-”, “*”, and “_” remain the same.

  • The space character “ ” is converted into a plus sign “+”.

  • All other characters are unsafe and are first converted into 1 or more bytes using some encoding scheme. Each byte is then represented by the three-character string %xy, where xy is the two-digit hexadecimal representation of that byte. The recommended encoding scheme to use is UTF-8. However, for compatibility reasons, the platform’s default encoding is used when an encoding isn’t specified.

For example, using UTF-8 as the encoding scheme, the string "string ü@foo-bar" is converted to "string+%C3%BC%40foo-bar". In UTF-8, character ü is encoded as 2 bytes C3 (hex) and BC (hex); and character @ is encoded as 1 byte 40 (hex).

URLEncoder declares the following class method for encoding a string:
String encode(String s, String enc)

This method translates the String argument passed to s into application/x-www-form-urlencoded format using the encoding scheme specified by enc. It uses the supplied encoding scheme to obtain the bytes for unsafe characters and throws java.io.UnsupportedEncodingException when enc’s value isn’t supported.

URLDecoder applies the following decoding rules:
  • Alphanumeric characters (a–z, A–Z, and 0–9) remain the same.

  • Special characters “.”, “-”, “*”, and “_” remain the same.

  • The plus sign “+” is converted into a space character “ ”.

  • A sequence of the form %xy will be treated as representing a byte, where xy is the two-digit hexadecimal representation of the 8 bits. Then, all substrings containing one or more of these byte sequences consecutively will be replaced by the character(s) whose encoding would result in those consecutive bytes. The encoding scheme used to decode these characters may be specified; when unspecified, the platform’s default encoding is used.

URLDecoder declares the following class method for decoding an encoded string:
String decode(String s, String enc)

This method decodes an application/x-www-form-urlencoded string using the encoding scheme specified by enc. The supplied encoding is used to determine what characters are represented by any consecutive sequences of the form %xy. UnsupportedEncodingException is thrown when enc’s value isn’t supported.

There are two possible ways in which the decoder could deal with illegally encoded strings. It could either leave illegal characters alone or it could throw IllegalArgumentException. The approach the decoder takes is left to the implementation.

Note

The World Wide Web Consortium recommends that UTF-8 should be used as the encoding scheme for encode() and decode(); see www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars. Not doing so may introduce incompatibilities.

We’ve created an ED (encode/decode) application that demonstrates URLEncoder and URLDecoder in the context of the previous "string ü@foo-bar" and "string+%C3%BC%40foo-bar" example. Listing 13-6 presents the application’s source code.
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class ED{
   public static void main(String[] args) throws UnsupportedEncodingException {
      String encodedData = URLEncoder.encode("string ü@foo-bar", "UTF-8");
      System.out.println(encodedData);
      System.out.println(URLDecoder.decode(encodedData, "UTF-8"));
   }
}
Listing 13-6

Encoding and Decoding an Encoded String

When you run this application, it generates the following output:
string+%C3%BC%40foo-bar
string ü@foo-bar
Note

Check out Wikipedia’s “Percent-encoding” topic (http://en.wikipedia.org/wiki/Percent-encoding) to learn more about URL encoding (and the more accurate percent-encoding term).

URI

The URI class represents URIs (such as URNs and URLs). It doesn’t provide access to a resource when the URI is a URL.

A URI instance stores a character string that conforms to the following syntax at the highest level:
[scheme:]scheme-specific-part[#fragment]

This syntax reveals that every URI optionally begins with a scheme followed by a colon character, where a scheme can be thought of as an application-level protocol for obtaining an Internet resource. However, this definition is too narrow because it implies that the URI is always a URL. A scheme can have nothing to do with resource location. For example, urn is the scheme for identifying URNs.

A scheme is followed by a scheme-specific-part that provides an instance of the scheme. For example, given the http://tutortutor.ca URI, tutortutor.ca is an instance of the http scheme. Scheme-specific-parts conform to the allowable syntax of their schemes and to the overall syntax structure of a URI (including what characters can be specified literally and what characters must be encoded).

A scheme concludes with an optional #-prefixed fragment level, which is a short string of characters that refers to a resource subordinate to another primary resource. The primary resource is identified by a URI; the fragment points to the subordinate resource. For example, http://example.com/document.txt#line=5,10 could identify lines 5 through 10 of a text document named document.txt on some website.

For more details, please consult the API documentation of class URI.

Accessing Network Interfaces and Interface Addresses

The NetworkInterface class represents a network interface in terms of a name (such as le0) and a list of IP addresses assigned to this interface. Although a network interface is often implemented on a physical NIC, it also can be implemented in software, for example, the loopback interface (which is useful for testing a client).

You can use the class’s methods to gather useful information about your platform’s network interfaces. For example, Listing 13-7 presents an application that iterates over all network interfaces, invoking the methods that obtain the network interface’s name and display name, determine if the network interface is a loopback interface, determine if the network interface is up and running, obtain the MTU, determine if the network interface supports multicasting, and enumerate all of the network interface’s virtual subinterfaces.
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
public class NetInfo {
   public static void main(String[] args) throws SocketException {
      Enumeration<NetworkInterface> eni;
      eni = NetworkInterface.getNetworkInterfaces();
      for (NetworkInterface ni: Collections.list(eni)) {
         System.out.println("Name = " + ni.getName());
         System.out.println("Display Name = " + ni.getDisplayName());
         System.out.println("Loopback = " + ni.isLoopback());
         System.out.println("Up and running = " + ni.isUp());
         System.out.println("MTU = " + ni.getMTU());
         System.out.println("Supports multicast = " + ni.supportsMulticast());
         System.out.println("Sub-interfaces");
         Enumeration<NetworkInterface> eni2;
         eni2 = ni.getSubInterfaces();
         for (NetworkInterface ni2: Collections.list(eni2))
            System.out.println("   " + ni2);
         System.out.println();
      }
   }
}
Listing 13-7

Enumerating All Network Interfaces

Tip

The java.util.Collections class’s ArrayList<T> list(Enumeration<T> enumeration) method is useful for converting a legacy enumeration to a modern array list.

Compile Listing 13-7 (javac NetInfo.java ) and execute this application (java NetInfo). When we run NetInfo on our Windows platform, we observe information that begins with the following output:
Name = lo
Display Name = Software Loopback Interface 1
Loopback = true
Up and running = true
MTU = -1
Supports multicast = true
Sub-interfaces
Name = net0
Display Name = WAN Miniport (SSTP)
Loopback = false
Up and running = false
MTU = -1
Supports multicast = true
Sub-interfaces

The complete output reveals a different MTU size for a few network interfaces. Each size represents the maximum length of a message that can fit into an IP datagram without needing to fragment the message into multiple IP datagrams. This fragmentation has performance implications, especially in the context of networked games. For this reason alone, the getMTU() method is a valuable member of NetworkInterface.

The getInterfaceAddresses() method returns a list of InterfaceAddress objects, with each object containing a network interface’s IP address along with broadcast address and subnet mask (IPv4) or network prefix length (IPv6).

Listing 13-8, which extends Listing 13-7 (with a few lines removed), enumerates all network interfaces, outputting their display names, and enumerates each network interface’s interface addresses, outputting interface address information.
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
public class NetInfo {
   public static void main(String[] args) throws SocketException {
      Enumeration<NetworkInterface> eni;
      eni = NetworkInterface.getNetworkInterfaces();
      for (NetworkInterface ni: Collections.list(eni)) {
         System.out.println("Name = " + ni.getName());
         List<InterfaceAddress> ias = ni.getInterfaceAddresses();
         Iterator<InterfaceAddress> iter = ias.iterator();
         while (iter.hasNext())
            System.out.println(iter.next());
         System.out.println();
      }
   }
}
Listing 13-8

Enumerating All Network Interfaces and Interface Addresses

Compile Listing 13-8 (javac NetInfo.java) and execute this application (java NetInfo). When we run NetInfo on our Windows platform, we observe the following information:
Name = lo
/127.0.0.1/8 [/127.255.255.255]
/0:0:0:0:0:0:0:1/128 [null]
Name = net0
Name = net1
Name = net2
Name = ppp0
Name = eth0
Name = eth1
Name = eth2
Name = ppp1
Name = net3
Name = eth3
/192.xxx.xxx.xxx/xx [/192.xxx.xxx.xxx]
/fe80:0:0:0:xxxx:xxxx:xxxx:xxxx%xx/xx [null]
Name = net4
/fe80:0:0:0:0:xxxx:xxxx:xxxx%xx/xxx [null]
Name = net5
/2001:0:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/x [null]
/fe80:0:0:0:xxxx:xxxx:xxxx:xxxx%xx/xx [null]
Name = eth4
Name = eth5
Name = eth6
Name = eth7
Name = eth8

Managing Cookies

Server applications commonly use HTTP cookies (state objects)—cookies for short—to persist small amounts of information on clients. For example, the identifiers of currently selected items in a shopping cart can be stored as cookies. It’s preferable to store cookies on the client rather than on the server because of the potential for millions of cookies (depending on a website’s popularity). In that case, not only would a server require a massive amount of storage just for cookies, but also searching for and maintaining cookies would be time-consuming.

Note

Check out Wikipedia’s “HTTP cookie” entry (http://en.wikipedia.org/wiki/HTTP_cookie) for a quick refresher on cookies.

A server application sends a cookie to a client as part of an HTTP response. A client (such as a web browser) sends a cookie to the server as part of an HTTP request. Before Java 5, applications worked with the URLConnection class (and its HttpURLConnection subclass) to get an HTTP response’s cookies and to set an HTTP request’s cookies. The String getHeaderFieldKey(int n) and String getHeaderField(int n) methods were used to access a response’s Set-Cookie headers, and the void setRequestProperty(String key, String value) method was used to create a request’s Cookie header.

Note

“RFC 2109: HTTP State Management Mechanism” (www.ietf.org/rfc/rfc2109.txt) describes the Set-Cookie and Cookie headers.

Java 5 introduced the abstract CookieHandler class as a callback mechanism that connects HTTP state management to an HTTP protocol handler (think concrete HttpURLConnection subclass). An application installs a concrete CookieHandler subclass as the system-wide cookie handler via the CookieHandler class’s void setDefault(CookieHandler cHandler) class method. A companion CookieHandler getDefault() class method returns this cookie handler, which is null when a system-wide cookie handler hasn’t been installed.

An HTTP protocol handler accesses response and request headers. This handler invokes the system-wide cookie handler’s void put(URI uri, Map<String, List<String>> responseHeaders) method to store response cookies in a cookie cache and invokes the Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) method to fetch request cookies from this cache. Unlike Java 5, Java 6 introduced a concrete implementation of CookieHandler so that HTTP protocol handlers and applications can work with cookies.

The concrete CookieManager class extends CookieHandler to manage cookies. This class does not interact with the web browser’s cookies (stored on the client computer). Instead, it represents a separate and distinct cookie manager.

A CookieManager object is initialized as follows:
  • With a cookie store for storing cookies. The cookie store is based on the CookieStore interface.

  • With a cookie policy for determining which cookies to accept for storage. The cookie policy is based on the CookiePolicy interface.

Create a cookie manager by calling either the CookieManager() constructor or the CookieManager(CookieStore store, CookiePolicy policy) constructor. The CookieManager() constructor invokes the latter constructor with null arguments, using the default in-memory cookie store and the default accept-cookies-from-the-original-server-only cookie policy. Unless you plan to create your own CookieStore and CookiePolicy implementations, you’ll most likely work with the default constructor. The following example creates and establishes a new CookieManager object as the system-wide cookie handler:
CookieHandler.setDefault(new CookieManager());
In contrast to the get() and put() methods of that class, which are called by HTTP protocol handlers, an application works with the getCookieStore() and setCookiePolicy() methods. Consider Listing 13-9.
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.URL;
import java.util.List;
public class ListAllCookies {
   public static void main(String[] args) throws IOException {
      if (args.length != 1) {
          System.err.println("usage: java ListAllCookies url");
          return;
      }
      CookieManager cm = new CookieManager();
      cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
      CookieHandler.setDefault(cm);
      new URL(args[0]).openConnection().getContent();
      List<HttpCookie> cookies = cm.getCookieStore().getCookies();
      for (HttpCookie cookie: cookies) {
           System.out.println("Name = " + cookie.getName());
           System.out.println("Value = " + cookie.getValue());
           System.out.println("Lifetime (seconds) = " + cookie.getMaxAge());
           System.out.println("Path = " + cookie.getPath());
           System.out.println();
      }
   }
}
Listing 13-9

Listing All Cookies for a Specific Domain

Listing 13-9 describes a command-line application that obtains and lists all cookies from its single domain name argument. After creating a cookie manager and invoking setCookiePolicy() to set the cookie manager’s policy to accept all cookies, ListAllCookies installs the cookie manager as the system-wide cookie handler. It next connects to the domain identified by the command-line argument and reads the content (via URL’s Object getContent() method).

The cookie store is obtained via getCookieStore() and used to retrieve all nonexpired cookies via its List<HttpCookie> getCookies() method. For each of these HttpCookies, String getName(), String getValue(), and other HttpCookie methods are invoked to return cookie-specific information.

Compile Listing 13-9 (javac ListAllCookies.java). The following output resulted from invoking java ListAllCookies http://java.dzone.com:
Name = SESS374e8db54ec3033c25a586b1d093b1d1
Value = irhqtiemls4cp0vf5pe1p0oeo7
Lifetime (seconds) = 2000000
Path = /
Note

For more information about cookie management, including examples that show you how to create your own CookiePolicy and CookieStore implementations, check out The Java Tutorials’ “Working With Cookies” lesson (http://docs.oracle.com/javase/tutorial/networking/cookies/index.html).

Exercises
The following exercises are designed to test your understanding of Chapter 13’s content:
  1. 1.

    Define network.

     
  2. 2.

    What is an intranet and what is an internet?

     
  3. 3.

    What do intranets and internets often use to communicate between nodes?

     
  4. 4.

    Define host.

     
  5. 5.

    What is a socket?

     
  6. 6.

    How is a socket identified?

     
  7. 7.

    Define IP address.

     
  8. 8.

    What is a packet?

     
  9. 9.

    A socket address is comprised of what elements?

     
  10. 10.

    Identify the InetAddress subclasses that are used to represent IPv4 and IPv6 addresses.

     
  11. 11.

    What is the loopback interface?

     
  12. 12.

    How is the local host represented?

     
  13. 13.

    Why are sockets based on the Socket class commonly referred to as stream sockets?

     
  14. 14.

    What does binding accomplish in the context of a Socket instance?

     
  15. 15.

    Define proxy. How does Java represent proxy settings?

     
  16. 16.

    True or false: The ServerSocket() constructor creates a bound sever socket.

     
  17. 17.

    What is the difference between the DatagramSocket and MulticastSocket classes?

     
  18. 18.

    What is the difference between unicasting and multicasting?

     
  19. 19.

    What is a URL?

     
  20. 20.

    What is a URN?

     
  21. 21.

    True or false: URLs and URNs are also URIs.

     
  22. 22.

    What is the equivalent of openStream()?

     
  23. 23.

    True or false: You need to invoke URLConnection’s void setDoInput(boolean doInput) method with true as the argument before you can input content from a web resource.

     
  24. 24.

    What does URLEncoder do when it encounters a space character?

     
  25. 25.

    What is the purpose of the URI class?

     
  26. 26.

    What does the NetworkInterface class accomplish?

     
  27. 27.

    What is a MAC address?

     
  28. 28.

    What does MTU stand for and what is its purpose?

     
  29. 29.

    True or false: NetworkInterface’s getName() method returns a human-readable name.

     
  30. 30.

    What does InterfaceAddress’s getNetworkPrefixLength() method return under IPv4?

     
  31. 31.

    Define HTTP cookie.

     
  32. 32.

    Why is it preferable to store cookies on the client rather than on the server?

     
  33. 33.

    Identify the four java.net types that are used to work with cookies.

     
  34. 34.

    Modify Listing 13-1’s EchoClient source code to explicitly close the socket.

     
  35. 35.

    Modify Listing 13-2’s EchoServer source code to exit the while loop and explicitly close the server socket when a file named kill appears in the directory from which the server was started. After this file appears, the server will probably not die immediately because it’s most likely waiting (via the accept() call) for an incoming client connection. However, it should die after servicing the next incoming connection.

     

Summary

A network is a group of interconnected nodes that can be shared among the network’s users. An intranet is a network located within an organization and an internet is a network connecting organizations to each other. The Internet is the global network of networks.

The java.net package provides types that support TCP/IP between processes running on the same or different hosts. Two processes communicate by way of sockets, which are endpoints in a communications link between these processes. Each endpoint is identified by an IP address that identifies a host and by a port number that identifies the process running on that host.

One process writes a message to its socket, the network management software portion of the underlying operating system breaks the message into a sequence of packets that it forwards to the other process’s socket, and the other process recombines received packets into the original message for its own processing.

The network management software uses TCP to create an ongoing conversation between two hosts in which messages are sent back and forth. Before this conversation occurs, a connection is established between these hosts. After the connection has been established, TCP enters a pattern where it sends message packets and waits for a reply that they arrived correctly (or for a timeout to expire when the reply doesn’t arrive because of some network problem). This pattern repeats and guarantees a reliable connection.

Because it can take time to establish a connection, and it also takes time to send packets (as it is necessary to receive reply acknowledgments, and also because of timeouts), TCP is slow. On the other hand, UDP, which doesn’t require connections and packet acknowledgment, is much faster. The downside is that UDP isn’t as reliable (there’s no guarantee of packet delivery, ordering, or protection against duplicate packets, although UDP uses checksums to verify that data is correct) because there’s no acknowledgment. Furthermore, UDP is limited to single-packet conversations.

An instance of a Socket-suffixed class is associated with a socket address comprised of an IP address and a port number. These classes often rely on the InetAddress class to represent the IPv4 or IPv6 address portion of the socket address, and represent the port number separately.

An instance of a Socket-suffixed class shares the concept of socket options, which are parameters for configuring socket behavior. Socket options are described by constants that are declared in the SocketOptions interface.

The Socket and ServerSocket classes support TCP-based communications between client processes and server processes. Socket supports the creation of client-side sockets, whereas ServerSocket supports the creation of server-side sockets.

The DatagramSocket and MulticastSocket classes let you perform UDP-based communications between a pair of hosts (DatagramSocket) or between as many hosts as necessary (MulticastSocket). With either class, you communicate one-way messages via datagram packets.

Two processes communicating via sockets demonstrate low-level network access. Java also supports high-level access via URLs that identify resources and specify where they are located on TCP-/IP-based networks.

URLs are represented by the URL class, which provides access to the resources to which they refer. URLConnection gives you additional control over client/server communication. For example, you can use this class to output content to various resources that accept content. In contrast, URL only lets you input content via openStream().

HTML lets you introduce forms into web pages that solicit information from page visitors. The java.net package provides URLEncoder and URLDecoder classes to assist you with the tasks of encoding and decoding form content.

URLs are a form of URI, which is a character string that identifies a resource without providing access to the resource or identifies a name. URIs are represented by the URI class, which provides methods for extracting parts of a URI (such as the scheme) and for performing normalization, resolution, and relativization operations.

The NetworkInterface class represents a network interface in terms of a name (such as le0) and a list of IP addresses assigned to this interface. NetworkInterface’s getInterfaceAddresses() method returns a list of InterfaceAddress objects, with each object containing a network interface’s IP address along with broadcast address and subnet mask (IPv4) or network prefix length (IPv6).

Server applications commonly use HTTP cookies (state objects)—cookies, for short—to persist small amounts of information on clients. Java provides the CookieHandler and CookieManager classes and the CookiePolicy and CookieStore interfaces for working with cookies.

This chapter focused on I/O in a network context. New I/O lets you perform file-based and network-based I/O in a more performant manner. Chapter 14 introduces you to Java’s new I/O APIs.

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

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