In this chapter you'll learn about Java's support of network programming. You'll learn the basics of client/server computing and TCP/IP socket programming, and then examine the classes of the java.net package and learn how to use them to develop client/server applications. This chapter provides an introduction to the java.net package. Other chapters in Part VIII, "Network Programming," explore the information presented here in greater detail.
The java.net package provides a set of classes that support network programming using the communication protocols employed by the Internet. These protocols are known as the Internet protocol suite and include the Internet Protocol (IP), the Transport Control Protocol (TCP), and the User Datagram Protocol (UDP) as well as other, less prominent supporting protocols. Although this section cannot provide a full description of the Internet protocols, it gives you the basic information that you need to get started with Java network programming. In order to take full advantage of this chapter, you will need an Internet connection.
Asking the question "What is the Internet?" may bring about a heated discussion in some circles. In this book, "the Internet" is defined as "the collection of all computers that can communicate, using the Internet protocol suite, with the computers and networks registered with the Internet Network Information Center (InterNIC)." This definition includes all computers to which you can directly send Internet Protocol packets (or indirectly, through a firewall).
Computers on the Internet communicate by exchanging packets of data, known as Internet Protocol (IP) packets. IP is the network protocol used to send information from one computer to another over the Internet. All computers on the Internet (by our definition in this book) communicate using IP, which moves information contained in IP packets. They are routed via special routing algorithms from a source computer that sends the packets to a destination computer that receives them. The routing algorithms figure out the best way to send the packets from source to destination.
In order for IP to send packets from a source computer to a destination computer, it must have some way of identifying these computers. All computers on the Internet are identified using one or more IP addresses. A computer may have more than one IP address if it has more than one interface to computers that are connected to the Internet.
IP addresses are 32-bit numbers. They may be written in decimal, hexadecimal, or other formats, but the most common format is dotted decimal notation. This format breaks the 32-bit address up into 4 bytes and writes each byte of the address as an unsigned decimal integer, separating them with dots. For example, one of my IP addresses is 0xCCD499C1. Because 0xCC = 204, 0xD4 = 212, 0x99 = 153, and 0xC1 = 193, my address in dotted decimal form is 204.212.153.193.
IP addresses are not easy to remember, even using dotted decimal notation. The Internet has adopted a mechanism, the Domain Name System (DNS), whereby computer names can be associated with IP addresses. These computer names are referred to as domain names. The DNS has several rules that determine how domain names are constructed and how they relate to one another. For the purposes of this chapter, it is sufficient to know that domain names are computer names and that they are mapped to IP addresses.
The mapping of domain names to IP addresses is maintained by a system of domain name servers. These servers can look up the IP address corresponding to a domain name. They also provide the capability to look up the domain name associated with a particular IP address, if one exists.
As mentioned, IP enables communication between computers on the Internet by routing data from a source computer to a destination computer. However, computer-to-computer communication only solves half of the network communication problem. In order for an application program, such as a mail program, to communicate with another application, such as a mail server, there needs to be a way to send data to specific programs within a computer.
Ports, or addresses within a computer, are used to enable communication between programs. Port addresses are 16-bit addresses that are usually associated with a particular application protocol. An application server, such as a Web server or an FTP server, listens on a particular port for service requests, performs whatever service is requested of it, and returns information to the port used by the application program requesting the service.
Popular Internet application protocols are associated with well-known ports. The server programs that implement these protocols listen on these ports for service requests. The well-known ports for some common Internet application protocols are shown in Table 30.1.
Port | Protocol |
21 | File transfer protocol |
23 | Telnet protocol |
25 | Simple mail transfer protocol |
80 | Hypertext transfer protocol |
The well-known ports are used to standardize the location of Internet services.
Transport protocols are used to deliver information from one port to another and thereby enable communication between application programs. They use either a connection- oriented or connectionless method of communication. TCP is a connection-oriented protocol, and UDP is a connectionless transport protocol.
The TCP connection-oriented protocol establishes a communication link between a source port/IP address and a destination port/IP address. The ports are bound together via this link until the connection is terminated and the link is broken. An example of a connection-oriented protocol is a telephone conversation. A telephone connection is established, communication takes place, and then the connection is terminated.
The reliability of the communication between the source and destination programs is ensured through error-detection and error-correction mechanisms that are implemented within TCP. TCP implements the connection as a stream of bytes from source to destination. This feature allows the use of the stream I/O classes provided by java.io.
The UDP connectionless protocol differs from the TCP connection-oriented protocol in that it does not establish a link for the duration of the connection. An example of a connectionless protocol is postal mail. To mail something, you just write down a destination address (and an optional return address) on the envelope of the item you're sending and drop it into a mailbox. When using UDP, an application program writes the destination port and IP address on a datagram and then sends the datagram to its destination. UDP is less reliable than TCP because there are no delivery-assurance or error-detection-and-correction mechanisms built into the protocol.
Application protocols such as FTP, SMTP, and HTTP use TCP to provide reliable, stream-based communication between client and server programs. Other protocols, such as the Time Protocol, use UDP because speed of delivery is more important than end-to-end reliability.
Most TCP/IP communication is unicast--packets are sent from a source host to a destination host in a point-to-point fashion. Unicast communication is used by the majority of Internet services. However, there are some applications where it is desirable for a host to be able to simultaneously send IP packets to multiple destination hosts--for example, to transmit an audio or video stream. This form of communication is known as multicast. Multicast communication enables a host to transmit IP packets to multiple hosts, referred to as a host group, using a single destination IP address.
Host groups may be permanent or temporary. Permanent groups are assigned fixed IP addresses. Temporary groups are dynamically assigned IP address. Hosts may join or leave a host group in a dynamic fashion--even permanent groups. The existence of a host group is independent of its members. Multicast routers are used to send IP multicast packets to the members of host groups.
The Internet provides a variety of services that contribute to its appeal. These services include email, newsgroups, file transfer, remote login, and the Web. Internet services are organized according to a client/server architecture. Client programs, such as Web browsers and file transfer programs, create connections to servers, such as Web and FTP servers. The clients make requests of the server, and the server responds to the requests by providing the service requested by the client.
The Web provides a good example of client/server computing. Web browsers are the clients, and Web servers are the servers. Browsers request HTML files from Web servers on your behalf by establishing a connection with a Web server and submitting file requests to the server. The server receives the file requests, retrieves the files, and sends them to the browser over the established connection. The browser receives the files and displays them on your browser window.
Clients and servers establish connections and communicate via sockets. Connections are communication links that are created over the Internet using TCP. Some client/server applications are also built around the connectionless UDP. These applications also use sockets to communicate.
Sockets are the endpoints of Internet communication. Clients create client sockets and connect them to server sockets. Sockets are associated with a host address and a port address. The host address is the IP address of the host where the client or server program is located. The port address is the communication port used by the client or server program. Server programs use the well-known port number associated with their application protocol.
A client communicates with a server by establishing a connection to the socket of the server. The client and server then exchange data over the connection. Connection- oriented communication is more reliable than connectionless communication because the underlying TCP provides message-acknowledgment, error-detection, and error-recovery services.
When a connectionless protocol is used, the client and server communicate by sending datagrams to each other's sockets. The UDP is used for connectionless protocols. It does not support reliable communication like TCP.
The java.net package provides several classes that support socket-based client/server communication.
The InetAddress class encapsulates Internet IP addresses and supports conversion between dotted decimal addresses and host names.
The Socket, ServerSocket, DatagramSocket, and MulticastSocket classes implement client and server sockets for connection-oriented and connectionless communication. The DatagramPacket class is used to construct UDP datagram packets. The SocketImpl and DatagramSocketImpl classes and the SocketImplFactory interface provide hooks for implementing custom sockets.
The URL, URLConnection, HttpURLConnection, and URLEncoder classes implement high-level browser-server Web connections. The ContentHandler and URLStreamHandler classes are abstract classes that have provided the basis for the implementation of Web content and stream handlers. They are supported by the ContentHandlerFactory and URLStreamHandlerFactory interfaces.
The FileNameMap interface is used to map filenames to MIME types. You'll learn about MIME types later in this chapter in the section "ContentHandler, ContentHandlerFactory, and FileNameMap."
The InetAddress class encapsulates Internet addresses. It supports both numeric IP addresses and host names.
The InetAddress class has no public variables or constructors. It provides 10 access methods that support common operations on Internet addresses. Three of these methods are static.
The getLocalHost()method is a static method that returns an InetAddress object that represents the Internet address of the local host computer. The static getByName()-method returns an InetAddress object for a specified host. The static getAllByName()method returns an array of all Internet addresses associated with a particular host.
The getAddress() method gets the numeric IP address of the host identified by the InetAddress object, and the getHostName()method gets its domain name. The getHostAddress()method returns the numeric IP address of an InetAddress object as a dotted decimal string. The isMulticastAddress() method returns a boolean value that indicates whether an InetAddress object represents a multicast address.
The equals(), hashCode(), and toString() methods override those of the Object class.
The NSLookupApp program illustrates the use of the InetAddress class. It takes a host name as a parameter and identifies the primary IP address associated with that host (see Listing 30.1).
import java.net.InetAddress;
import java.net.UnknownHostException; import java.lang.System; public class NSLookupApp { public static void main(String args[]) { try { if(args.length!=1){ System.out.println("Usage: java NSLookupApp hostName"); return; } InetAddress host = InetAddress.getByName(args[0]); String hostName = host.getHostName(); System.out.println("Host name: "+hostName); System.out.println("IP address: "+host.getHostAddress()); }catch(UnknownHostException ex) { System.out.println("Unknown host"); return; } }
}
Compile NSLookupApp and run it as follows:
java NSLookupApp sun.com
Host name: sun.com IP address: 192.9.9.100
This code example uses NSLookupApp to look up the primary IP address associated with the sun.com host. Try it with other Internet host names to look up their IP addresses.
NSLookupApp consists of a single main() method. A try statement surrounds most of the program's statements. It is used to catch the UnknownHostException, which is generated when an invalid host name is entered by the user or when a host name cannot be looked up from a DNS server.
NSLookupApp first checks the number of arguments supplied in the program invocation to make sure that a host name argument is provided by the user. It then uses the host name string of the first user argument with the static getByName() method of the InetAddress class to create an InetAddress object based on the user-supplied host name. This InetAddress object is assigned to the host variable. The getHostName() method gets the host's name from the host variable and assigns it to the hostName variable. The getHostAddress() method returns the four bytes of the host's IP address as a dotted decimal string. The host name and IP address then are printed.
The Socket class implements client connection-based sockets. These sockets are used to develop applications that utilize services provided by connection-oriented server applications.
The Socket class provides eight constructors that create sockets and optionally connect them to a destination host and port. Two of these constructors were deprecated in JDK 1.1, but they still appear in JDK 1.2. The DatagramSocket constructor is the preferred constructor for creating UDP sockets.
The access methods of the Socket class are used to access the I/O streams and connection parameters associated with a connected socket. The getInetAddress() and getPort() methods get the IP address of the destination host and the destination host port number to which the socket is connected. The getLocalPort() method returns the source host local port number associated with the socket. The getLocalAddress() method returns the local IP address associated with the socket. The getInputStream() and getOutputStream() methods are used to access the input and output streams associated with a socket. The close() method is used to close a socket.
The getSoLinger() and setSoLinger() methods are used to get and set a socket's SO_LINGER option, which identifies how long a socket is to remain open after a close() method has been invoked and data remains to be sent over the socket.
The getSoTimeout() and setSoTimeout() methods are used to get and set a socket's SO_TIMEOUT option, which is used to identify how long a read operation on the socket is to be blocked before it times out and the blocking ends.
The getTcpNoDelay() and setTcpNoDelay() methods are used to get and set a socket's TCP_NODELAY option, which is used to specify whether Nagle's algorithm should be used to buffer data that is sent over a socket connection. When TCP_NODELAY is true, Nagle's algorithm is disabled.
The setSocketImplFactory() class method is used to switch from the default Java socket implementation to a custom socket implementation. The toString() method returns a string representation of the socket.
The PortTalkApp program is used to talk to a particular port on a given host on a line-by-line basis. It provides the options of sending a line to the specified port, receiving a line from the other host, or terminating the connection. Its source code is shown in Listing 30.2.
import java.lang.System;
import java.net.Socket; import java.net.InetAddress; import java.net.UnknownHostException; import java.io.*; public class PortTalkApp { public static void main(String args[]){ PortTalk portTalk = new PortTalk(args); portTalk.displayDestinationParameters(); portTalk.displayLocalParameters(); portTalk.chat(); portTalk.shutdown(); } } class PortTalk { Socket connection; DataOutputStream outStream; BufferedReader inStream; public PortTalk(String args[]){ if(args.length!=2) error("Usage: java PortTalkApp host port"); String destination = args[0]; int port = 0; try { port = Integer.valueOf(args[1]).intValue(); }catch (NumberFormatException ex){ error("Invalid port number"); } try{ connection = new Socket(destination,port); }catch (UnknownHostException ex){ error("Unknown host"); } catch (IOException ex){ error("IO error creating socket"); } try{ inStream = new BufferedReader( new InputStreamReader(connection.getInputStream())); outStream = new DataOutputStream(connection.getOutputStream()); }catch (IOException ex){ error("IO error getting streams"); } System.out.println("Connected to "+destination+" at port "+port+"."); } public void displayDestinationParameters(){ InetAddress destAddress = connection.getInetAddress(); String name = destAddress.getHostName(); byte ipAddress[] = destAddress.getAddress(); int port = connection.getPort(); displayParameters("Destination ",name,ipAddress,port); } public void displayLocalParameters(){ InetAddress localAddress = null; try{ localAddress = InetAddress.getLocalHost(); }catch (UnknownHostException ex){ error("Error getting local host information"); } String name = localAddress.getHostName(); byte ipAddress[] = localAddress.getAddress(); int port = connection.getLocalPort(); displayParameters("Local ",name,ipAddress,port); } public void displayParameters(String s,String name, Âbyte ipAddress[],int port){ System.out.println(s+"host is "+name+"."); System.out.print(s+"IP address is "); for(int i=0;i<ipAddress.length;++i) System.out.print((ipAddress[i]+256)%256+"."); System.out.println(); System.out.println(s+"port number is "+port+"."); } public void chat(){ BufferedReader keyboardInput = new BufferedReader( new InputStreamReader(System.in)); boolean finished = false; do { try{ System.out.print("Send, receive, or quit (S/R/Q): "); System.out.flush(); String line = keyboardInput.readLine(); if(line.length()>0){ line=line.toUpperCase(); switch (line.charAt(0)){ case `S': String sendLine = keyboardInput.readLine(); outStream.writeBytes(sendLine); outStream.write(13); outStream.write(10); outStream.flush(); break; case `R': int inByte; System.out.print("***"); while ((inByte = inStream.read()) != `\n') System.out.write(inByte); System.out.println(); break; case `Q': finished=true; break; default: break; } } }catch (IOException ex){ error("Error reading from keyboard or socket"); } } while(!finished); } public void shutdown(){ try{ connection.close(); }catch (IOException ex){ error("IO error closing socket"); } } public void error(String s){ System.out.println(s); System.exit(1); }
}
To see how PortTalkApp works, run it using the following command line:
java PortTalkApp jaworski.com 7
Connected to jaworski.com at port 7. Destination host is jaworski.com. Destination IP address is 204.212.153.193. Destination port number is 7. Local host is biscuit.jaworski.com. Local IP address is 204.212.153.198. Local port number is 1237. Send, receive, or quit (S/R/Q):
PortTalkApp connects to my server at port 7. This is the port number for the echo server application. It is used to test Internet communication between hosts. It identifies my host's name, IP address, and destination port number. In this example, I am connecting from another computer on my local area network. Its name is biscuit.jaworski.com and it has the 204.212.153.198 IP address. When you run the program, your host name and IP address will be displayed. The local port number that I am connecting from is port 1237.
PortTalkApp asks you whether you want to send a line, receive a line, or quit the program. Whether you elect to send or receive is important. If you decide to receive a line and the host is not sending any data, your program will block while it waits to receive information from a socket-based stream.
Enter an S to send a line and then enter This is a test! on the following line, like this:
Send, receive, or quit (S/R/Q): s
This is a test! Send, receive, or quit (S/R/Q):
PortTalkApp will send your line to port 7 on my host and then prompt you for your next command. Enter R to receive a line of text from my server:
Send, receive, or quit (S/R/Q): r
***This is a test! Send, receive, or quit (S/R/Q):
PortTalkApp reads a line of text from the socket stream and displays it, prefixed with three asterisks. Now enter Q to close the connection and terminate the program. You can also use PortTalkApp to talk to other ports. For example, you can use it to talk to port 25 of hosts that support the Simple Mail Transport Protocol in order to send email to someone who is served by that host.
PortTalkApp consists of a simple main() function that creates an object of class PortTalk, passing it the user-supplied host and port arguments. It invokes the displayDestinationHostParameters() and displayLocalParameters() methods of the PortTalk class to provide the initial connection-status information. The chat() method is used to send and receive lines of text over an established connection. The shutdown() method terminates the connection.
The PortTalk class implements the bulk of the processing performed by the program. It declares three field variables. The connection variable keeps track of the socket used with the connection. The inStream and outStream variables maintain the input and output streams derived from the socket.
The PortTalk constructor checks the arguments supplied by the user to make sure that a host and port number were supplied, and converts the user-supplied port number to an integer. The error() method is used to display any errors to the console window. A new Socket object is created using the specified destination host name and port number and is assigned to the connection variable. The getInputStream() and getOutputStream() methods of the Socket class are used to attach input and output streams to the socket identified by the connection variable. These streams are then filtered as BufferedReader and DataOutputStream objects and assigned to the inStream and outStream variables. The constructor ends by displaying a connection status message to the console window.
The displayDestinationParameters() method uses the getInetAdress() method of the Socket class to get the InetAddress object associated with the destination host of the connection. It uses the getHostName() and getAddress() methods of the InetAddress class to obtain the name and IP address of the destination host. The getPort() method of the Socket class is used to get the destination port number. These parameters are displayed using the displayParameters() method.
The displayLocalParameters() method uses the getLocalHost(), getHostName(), and getAddress() methods of the InetAddress class to obtain the InetAddress object, name, and IP address of the local host. The getLocalPort() method of the Socket class is used to get the local port number. These parameters are displayed using the displayParameters() method.
The displayParameters() method displays the host name, IP address, and port number of an end of a socket connection. The s string parameter is used to differentiate between a local host and a destination host.
The chat() method implements the heart of the PortTalkApp program. It displays the Send, receive, or quit (S/R/Q): prompt to the user and then reads an input line from the user's keyboard.
If the user enters S to send, another line is read from the user's keyboard. This line is then written to the output stream associated with the socket connection. A carriage return and a line-feed character are then written to the output stream to signal the end of the line. The carriage return-linefeed combination is the standard end-of-line identifier used with Internet application protocols.
If the user enters R to receive, three asterisks (***) are written to the console window to indicate input from the destination host. Then the input stream associated with the socket is read, a byte at a time, and displayed to the console window until a newline (\n) character is encountered.
If the user enters O to quit, the do loop of the chat() method is terminated.
The shutdown() method closes the Socket object referenced by the connection variable.
The error() method prints an error message to the console window and then terminates the program using the exit() method of the System class.
The ServerSocket class implements a TCP server socket. It provides three constructors that specify the port to which the server socket is to listen for incoming connection requests, an optional maximum connection request queue length, and an optional Internet address. The Internet address argument allows multihomed hosts (that is, hosts with more than one Internet address) to limit connections to a specific interface.
The accept() method is used to cause the server socket to listen and wait until an incoming connection is established. It returns an object of class Socket once a connection is made. This Socket object is then used to carry out a service for a single client. The getInetAddress() method returns the address of the host to which the socket is connected. The getLocalPort() method returns the port on which the server socket listens for an incoming connection. The toString()method returns the socket's address and port number as a string in preparation for printing.
The getSoTimeout() and setSoTimeout() methods set the socket's SO_TIMEOUT parameter. The close() method closes the server socket.
The static setSocketFactory() method is used to change the default ServerSocket implementation to a custom implementation. The implAccept() method is used by subclasses of ServerSocket to override the accept() method.
The ReverServerApp program is a simple server that listens on port 1234 for incoming connections from client programs. When ReverServerApp connects to a client it reads one line of text at a time from the client, reverses the characters in the text line, and sends them back to the client. The source code of ReverServerApp is shown in Listing 30.3.
import java.lang.System;
import java.net.ServerSocket; import java.net.Socket; import java.io.*; public class ReverServerApp { public static void main(String args[]){ try{ ServerSocket server = new ServerSocket(1234); int localPort = server.getLocalPort(); System.out.println("Reverse Server is listening on port "+ ÂlocalPort+"."); Socket client = server.accept(); String destName = client.getInetAddress().getHostName(); int destPort = client.getPort(); System.out.println("Accepted connection to "+destName+" on port "+ destPort+"."); BufferedReader inStream = new BufferedReader( new InputStreamReader(client.getInputStream())); DataOutputStream outStream = Ânew DataOutputStream(client.getOutputStream()); boolean finished = false; do { String inLine = inStream.readLine(); System.out.println("Received: "+inLine); if(inLine.equalsIgnoreCase("quit")) finished=true; String outLine=new ReverseString(inLine.trim()).getString(); for(int i=0;i<outLine.length();++i) outStream.write((byte)outLine.charAt(i)); outStream.write(13); outStream.write(10); outStream.flush(); System.out.println("Sent: "+outLine); } while(!finished); inStream.close(); outStream.close(); client.close(); server.close(); }catch (IOException ex){ System.out.println("IOException occurred."); } } } class ReverseString { String s; public ReverseString(String in){ int len = in.length(); char outChars[] = new char[len]; for(int i=0;i<len;++i) outChars[len-1-i]=in.charAt(i); s = String.valueOf(outChars); } public String getString(){ return s; }
}
To see how ReverServerApp works, you need to run it in a separate window and then use PortTalkApp to feed it lines of text. First, run ReverServerApp using the following command line:
java ReverServerApp
Reverse Server is listening on port 1234.
ReverServerApp notifies you that it is up and running. In a separate window, run PortTalkApp as follows, supplying your host name instead of athome.jaworski.com:
java PortTalkApp athome.jaworski.com 1234
Connected to athome.jaworski.com at port 1234. Destination host is athome.jaworski.com. Destination IP address is 204.212.153.194. Destination port number is 1234. Local host is athome.jaworski.com. Local IP address is 204.212.153.194. Local port number is 1302. Send, receive, or quit (S/R/Q):
NOTE: You can use localhost or 127.0.0.1 as an IP address if you do not have a host name or cannot determine your IP address.
PortTalkApp displays all of the parameters of both endpoints of the connection. If you look in the window where ReverServerApp is running, you will see a message similar to the following:
Accepted connection to athome.jaworski.com on port 1302.
The port number reported by ReverServer is consistent with that reported by PortTalkApp. Now switch back to the PortTalkApp window and enter S to send a line of text, followed by the line of text This is a test!, as shown in the following output:
Send, receive, or quit (S/R/Q): s
This is a test!
The ReverServerApp window reports the following:
Received: This is a test!
Sent: !tset a si sihT
Enter an R in the PortTalkApp window, as shown in the following output:
Send, receive, or quit (S/R/Q): r
***!tset a si sihT Send, receive, or quit (S/R/Q):
PortTalkApp displays the text that it received from ReverServerApp. Enter the S command followed by a quit text line:
Send, receive, or quit (S/R/Q): s
quit
The quit line is read by ReverServerApp, causing it to terminate the connection and exit. It displays the following:
Received: quit
Sent: tiuq
In the PortTalkApp window, type Q to terminate PortTalkApp, as shown in the following output:
Send, receive, or quit (S/R/Q): q
The ReverServerApp program is smaller in size than PortTalkApp. It consists of a single main() method. The ReverseString class is also declared.
The main()method begins by creating a ServerSocket object on port 1234. It then uses the getLocalPort() method to get the local port number associated with the socket. This is to verify that it is indeed using port 1234. It then displays the fact that it is up and running and displays the number of the port on which it is listening for connections.
The accept() method is used to accept an incoming client connection and return the Socket object associated with the connection. The getHostName() and getPort() methods are used to get the host name and port number associated with the client program. These parameters are displayed to the console window. Input and output streams are then associated with the socket.
The main() method enters a loop, where it reads a line of text from the input stream and then checks to see if it is the quit termination signal. The ReverseString()constructor and getString() method are used to reverse the line read from the input stream. The reversed line is then written to the output stream. If the quit line was received from the client, the loop is terminated and the input stream, output stream, client socket, and server socket are closed.
The ReverseString class provides a constructor that reverses a string and a getString() method for retrieving the reversed string.
The DatagramSocket class implements client and server sockets using the UDP protocol. UDP is a connectionless protocol that allows application programs (both clients and servers) to exchange information using chunks of data known as datagrams.
DatagramSocket provides three constructors. The default constructor creates a datagram socket for use by client applications. No port number is specified. The second constructor allows a datagram socket to be created using a specified port. This constructor is typically used with server applications. The third constructor allows an Internet address to be specified in addition to the port. This is used to restrict service to a specific host interface.
The send() and receive() methods are used to send and receive datagrams using the socket. The datagrams are objects of class DatagramPacket. The getLocalPort() and getLocalAddress() methods return the local port and Internet address of the socket. The close() method closes this socket. The getSoTimeout() and setSoTimeout()methods get and set the socket's SO_TIMEOUT parameter.
The DatagramPacket class encapsulates the actual datagrams that are sent and received using objects of class DatagramSocket. Two different constructors are provided: one for datagrams that are received from a datagram socket, and one for creating datagrams that are sent over a datagram socket. The arguments to the received datagram constructor are a byte array used as a buffer for the received data, and an integer that identifies the number of bytes received and stored in the buffer. The sending datagram constructor adds two additional parameters: the IP address and port where the datagram is to be sent.
Eight access methods are provided. The getAddress() and getPort() methods are used to read the destination IP address and port of the datagram. The getLength() and getData() methods are used to get the number of bytes of data contained in the datagram and to read the data into a byte array buffer. The setAddress(), setPort(), setLength(), and setData() methods allow the datagram's IP address, port, length, and data values to be set.
The TimeServerApp and GetTimeApp programs illustrate the use of client/server computing using datagrams. TimeServerApp listens on a UDP socket on port 2345 for incoming datagrams. When a datagram is received, it displays the data contained in the datagram to the console window and returns a datagram with the current date and time to the sending client program. It terminates its operation when it receives a datagram with the text quit as its data.
The GetTimeApp program sends five datagrams with the text time in each datagram to local port 2345. After sending each datagram, it waits for a return datagram from TimeServerApp. It displays the datagrams that it sends and receives to the console window. It then sends a quit datagram to TimeServerApp and terminates its operation.
The TimeServerApp program listing is shown in Listing 30.4. The code for GetTimeApp is in Listing 30.5.
import java.lang.System;
import java.net.DatagramSocket; import java.net.DatagramPacket; import java.net.InetAddress; import java.io.IOException; import java.util.Date; public class TimeServerApp { public static void main(String args[]){ try{ DatagramSocket socket = new DatagramSocket(2345); String localAddress = InetAddress.getLocalHost().getHostName().trim(); int localPort = socket.getLocalPort(); System.out.print(localAddress+": "); System.out.println("Time Server is listening on port "+localPort+"."); int bufferLength = 256; byte outBuffer[]; byte inBuffer[] = new byte[bufferLength]; DatagramPacket outDatagram; DatagramPacket inDatagram = new DatagramPacket(inBuffer,inBuffer.length); boolean finished = false; do { socket.receive(inDatagram); InetAddress destAddress = inDatagram.getAddress(); String destHost = destAddress.getHostName().trim(); int destPort = inDatagram.getPort(); System.out.println("\nReceived a datagram from "+destHost+" at port "+ destPort+"."); String data = new String(inDatagram.getData()).trim(); System.out.println("It contained the data: "+data); if(data.equalsIgnoreCase("quit")) finished=true; String time = new Date().toString(); outBuffer=time.getBytes(); outDatagram = Ânew DatagramPacket(outBuffer,outBuffer.length,destAddress, destPort); socket.send(outDatagram); System.out.println("Sent "+time+" to "+destHost+" at port "+ ÂdestPort+"."); } while(!finished); }catch (IOException ex){ System.out.println("IOException occurred."); } }
}
import java.lang.System;
import java.net.DatagramSocket; import java.net.DatagramPacket; import java.net.InetAddress; import java.io.IOException; public class GetTimeApp { public static void main(String args[]){ try{ DatagramSocket socket = new DatagramSocket(); InetAddress localAddress = InetAddress.getLocalHost(); String localHost = localAddress.getHostName(); int bufferLength = 256; byte outBuffer[]; byte inBuffer[] = new byte[bufferLength]; DatagramPacket outDatagram; DatagramPacket inDatagram = Ânew DatagramPacket(inBuffer,inBuffer.length); for(int i=0;i<5;++i){ outBuffer = new byte[bufferLength]; outBuffer = "time".getBytes(); outDatagram = new DatagramPacket(outBuffer,outBuffer.length, localAddress,2345); socket.send(outDatagram); System.out.println("\nSent time request to "+localHost+ Â" at port 2345."); socket.receive(inDatagram); InetAddress destAddress = inDatagram.getAddress(); String destHost = destAddress.getHostName().trim(); int destPort = inDatagram.getPort(); System.out.println("Received a datagram from "+destHost+" at port "+ destPort+"."); String data = new String(inDatagram.getData()); data=data.trim(); System.out.println("It contained the following data: "+data); } outBuffer = new byte[bufferLength]; outBuffer = "quit".getBytes(); outDatagram = new DatagramPacket(outBuffer,outBuffer.length, localAddress,2345); socket.send(outDatagram); }catch (IOException ex){ System.out.println("IOException occurred."); } }
}
TimeServerApp and GetTimeApp should be run in separate windows. First, start TimeServerApp using the following command line:
java TimeServerApp
biscuit.jaworski.com: Time Server is listening on port 2345.
TimeServerApp will respond by letting you know that it is up and running and listening on port 2345.
Next, start GetTimeApp in a different window, as follows:
java GetTimeApp
Sent time request to biscuit.jaworski.com at port 2345. Received a datagram from 204.212.153.198 at port 2345. It contained the following data: Fri Dec 26 17:32:13 PST 1997 Sent time request to biscuit.jaworski.com at port 2345. Received a datagram from 204.212.153.198 at port 2345. It contained the following data: Fri Dec 26 17:32:14 PST 1997 Sent time request to biscuit.jaworski.com at port 2345. Received a datagram from 204.212.153.198 at port 2345. It contained the following data: Fri Dec 26 17:32:14 PST 1997 Sent time request to biscuit.jaworski.com at port 2345. Received a datagram from 204.212.153.198 at port 2345. It contained the following data: Fri Dec 26 17:32:14 PST 1997 Sent time request to biscuit.jaworski.com at port 2345. Received a datagram from 204.212.153.198 at port 2345. It contained the following data: Fri Dec 26 17:32:15 PST 1997
GetTimeApp reports the packets it sends to and receives from TimeServerApp and then terminates. TimeServerApp provides a similar display in its window, as shown in the following output:
Received a datagram from 204.212.153.198 at port 1325. It contained the data: time Sent Fri Dec 26 17:32:13 PST 1997 to 204.212.153.198 at port 1325. Received a datagram from 204.212.153.198 at port 1325. It contained the data: time Sent Fri Dec 26 17:32:14 PST 1997 to 204.212.153.198 at port 1325. Received a datagram from 204.212.153.198 at port 1325. It contained the data: time Sent Fri Dec 26 17:32:14 PST 1997 to 204.212.153.198 at port 1325. Received a datagram from 204.212.153.198 at port 1325. It contained the data: time Sent Fri Dec 26 17:32:14 PST 1997 to 204.212.153.198 at port 1325. Received a datagram from 204.212.153.198 at port 1325. It contained the data: time Sent Fri Dec 26 17:32:15 PST 1997 to 204.212.153.198 at port 1325. Received a datagram from 204.212.153.198 at port 1325. It contained the data: quit Sent Fri Dec 26 17:32:15 PST 1997 to 204.212.153.198 at port 1325.
These two simple programs illustrate the basic mechanics of datagram-based client/ server applications. A UDP client sends a datagram to a UDP server at the server's port address. The UDP server listens on its port for a datagram, processes the datagram, and sends back information to the UDP client.
TimeServerApp begins by creating a DatagramSocket object on port 2345 and assigning it to the socket variable. It then obtains the host name and local port number using the getHostName() and getLocalPort() methods and displays this information to the console window.
TimeServerApp creates two byte buffers--outBuffer and inBuffer. outBuffer is an empty buffer that is used to send data in outgoing datagrams. inBuffer is initialized to a blank 256-byte buffer. TimeServerApp then declares two variables of the DatagramPacket class--a non-initialized outDatagram variable and an inDatagram variable that is initialized using the inBuffer[] array.
TimeServerApp executes a loop where it receives and processes datagrams received from client programs. It receives datagrams using the receive() method of the DatagramSocket class. It uses the getAddress() and getPort() methods of the DatagramPacket class to get the host address and port of the client program that sent the socket. It displays this information to the console window. It uses the getData() method of the DatagramPacket class to retrieve the data sent by the client program. It converts this data to a string and displays it on the console window. If the received data contains the quit string, it sets the finished flag to true. TimeServerApp processes the client time request by using the Date() constructor of the java.util package to construct a new Date object, converting the Date object to a byte array, and storing the data in outBuffer. It then creates a new DatagramPacket object, using outBuffer, with the destination address and port number of the sending client program. It sends the datagram to the client using the send() method of the DatagramSocket class. The console display is then updated with the data that was sent to the client program.
The GetTimeApp client program creates a DatagramSocket object and assigns it to the socket variable. It then creates a DatagramPacket objects in the same manner as the TimeServerApp program. GetTimeApp uses a for statement to loop five times, sending five datagrams to port 2345 of the local host. After each datagram is sent, it waits to receive a return datagram from TimeServerApp. It uses the getAddress(), getPort(), and getData() methods of the DatagramPacket class to report this information to the console window.
After sending and receiving five datagrams, GetTimeApp sends a datagram with the quit text to tell TimeServerApp that it should terminate its processing.
The MulticastSocket class is used for developing clients and servers for IP multicasting. It provides the capability for hosts to join and leave multicast groups. All hosts in a multicast group receive UDP datagrams that are sent to the IP address of the group. Each host in the group listens on a common UDP port for the datagrams of the multicast application.
The MulticastSocket class has two constructors--a default parameterless constructor and a constructor that specifies the port number on which to listen for multicast datagrams. The MulticastSocket class has seven access methods:
The joinGroup() and leaveGroup() methods are used to join and leave a multicast group at a specified Internet address. The getInterface() and setInterface() methods are used to get and set the IP address of the host interface that is used for multicasting. The getTTL() and setTTL() methods are used to get and set the time-to-live for multicast packets that are sent on the multicast socket. Time-to-live specifies the number of times that a packet is forwarded before it expires. The send() method is used to send a datagram to multicast IP address.
The SocketImpl and DataSocketImpl classes are abstract classes that are used as a basis for defining custom socket implementations. The SocketImplFactory interface must be implemented by new socket implementations.
NOTE: The setSocketImplFactory() method of the Socket class can be used to set the system SocketImplFactory. Once it is set, it cannot be changed.
The SocketImpl class provides four variables that are used to define a socket: the destination IP address, the destination port, the local port, and a file descriptor used to create streams. The local IP address of the host need not be specified. The DatagramSocketImpl class uses only the local port and file descriptor because no connection with a destination IP address and port is required for UDP sockets.
Some of the access methods defined by SocketImpl are used to perform lower-level socket operations. These include listening for connections, accepting connections, binding a socket to a port, and implementing the actual connection. Other access methods are used to support stream-based I/O and to provide access to the IP address and port parameters of a socket. The DatagramSocketImpl class provides a similar set of methods that are oriented to the processing of UDP datagrams.
In addition to providing the basic TCP- and UDP-based sockets used by almost all Internet client/server applications, the java.net package provides a useful set of classes that support higher-level, Web-specific applications. These classes are centered around the URL class, which encapsulates an object on the Web, typically a Web page, by its URL address.
URL stands for uniform resource locator and, as its name states, provides a uniform way to locate resources on the Web. Different types of URLs are used with different application protocols, the most common of which are the Hypertext Transfer Protocol (HTTP) and the File Transfer Protocol (FTP). URLs for these types of protocols are mainly used to identify the location of files, such as Web pages, supporting images, multimedia files, text files, and downloadable programs. HTTP URLs also refer to executable programs, such as CGI scripts, which perform Web-related services. CGI scripts are programs, usually written in a scripting language, that receive input and generate output in accordance with the common gateway interface (CGI) specification.
The URL class encapsulates Web objects by their URL address. It provides a set of constructors that allow URL objects to be easily constructed, and a set of access methods that allow high-level read and write operations to be performed using URLs.
Most URLs, but not all, consist of a protocol, host name, and the path and name of a file on the host. For example, the URL http://www.jaworski.com/jdg/index.htm refers to a Web page on my Web server. It specifies the HTTP protocol as the protocol used to access the Web page. It identifies my host name as www.jaworski.com, and it names the file as /jdg/index.htm, where /jdg/ is the directory path to the file (relative to my Web server's directory root) and index.htm is the file's name. In HTTP URLs, the path/filename is optional. For example, the URL http://www.jaworski.com/jdg/ is equivalent to the previous URL. My Web server uses the file name index.htm as the default name for a file. The path name can also be omitted. The URL http://www.jaworski.com would use the index.htm file in the Web server's root directory.
The four URL constructors allow URL objects to be created using a variety of URL parameters, such as protocol type, host name, port, and file path. These parameters may be supplied separately or in text form as part of an URL string. The URL class treats a file's path and name as a single entity to provide a more convenient way of working with URL components.
URLs can be constructed using their absolute address or using an address that is relative to another URL. Up until now we have been working with the complete or absolute address of an URL. A relative address is a path/filename or file offset that is specified relative to an absolute URL. For example, the absolute URL http://www.jaworski.com can be combined with the relative URL /jdg/index.htm to produce the URL to http://www.jaworski.com/jdg/index.htm.
The URL access methods provide a full set of URL processing capabilities. The getProtocol(), getHost(), getPort(), getFile(), and getRef() methods allow the individual address components of the URL to be determined. The getContent() and openStream() methods allow reading of the Web object pointed to by the URL. The toExternalForm() and toString()methods enable URLs to be converted into strings to support display and printing. The equals()method compares URLs, and the sameFile() method compares the Web objects pointed to by the URLs. The openConnection() method creates an object of class URLConnection to the Web object pointed to be the URL. This class is discussed in the "URLConnection and HttpURLConnection" section later in this chapter.
The GetURLApp program illustrates the power provided by the URL class. This small program implements a primitive Web browser. Just run the program with the name of an URL, and it makes a connection to the destination Web server and downloads the referenced document. The program's source code is shown in Listing 30.6.
import java.lang.System;
import java.net.URL; import java.net.MalformedURLException; import java.io.*; public class GetURLApp { public static void main(String args[]){ try{ if(args.length!=1) error("Usage: java GetURLApp URL"); System.out.println("Fetching URL: "+args[0]); URL url = new URL(args[0]); BufferedReader inStream = new BufferedReader( new InputStreamReader(url.openStream())); String line; while ((line = inStream.readLine())!= null){ System.out.println(line); } inStream.close(); }catch (MalformedURLException ex){ error("Bad URL"); }catch (IOException ex){ error("IOException occurred."); } } public static void error(String s){ System.out.println(s); System.exit(1); }
}
After compiling the program, try running it with the URL http://www.jaworski.com/ java/GetURLApp.htm as follows. Make sure that you use the correct uppercase and lowercase characters:
java GetURLApp http://www.jaworski.com/java/GetURLApp.htm
The program will respond by displaying the following Web document from my Web server:
java GetURLApp http://www.jaworski.com/java/GetURLApp.htm
Fetching URL: http://www.jaworski.com/java/GetURLApp.htm <!DOCTYPE HTML PUBLIC "-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN"> <HTML><HEAD><TITLE>GetURLApp Test Results</TITLE></HEAD> <BODY><H1>GetURLApp Test Results</H1> <P>Congratulations! You were able to successfully compile and run ÂGetURLApp. </P> </BODY></HTML>
Try running the program with other URLs to see how they are displayed.
GetURLApp consists of a short main() method and the error() method, used to display error messages to the console window.
The main() method checks the arguments supplied by the user to make sure that the correct number of arguments are present. It then displays a message to the console window identifying the URL that it is trying to fetch. It creates an URL object using the URL name supplied by the user and assigns it to the url variable. It then uses the openStream() method of the URL class to create an input stream from the URL. The input stream is filtered as a BufferedReader object and is assigned to the inStream variable. The inStream variable is used to read and display the input stream one line at a time.
The URLConnnection class is an abstract class that encapsulates an active HTTP connection to a Web object represented by an URL. It provides a number of methods for getting information about the Web object and about the connection to the Web object, and for interacting with the Web object.
URLConnection defines several class variables that specify the connection state and associated parameters. It also supplies numerous methods that provide access to the HTTP-specific fields of the connection. This class is studied, in detail, in Chapter 31, "Client Programs." The next programming example covers a few aspects of its use.
The HttpURLConnection class is a subclass of URLConnection that provides direct access to the HTTP parameters involved in a client/server HTTP connection. HttpURLConnection is also covered in Chapter 31.
The URLEncoder class is a simple class that provides a single static method, encode(), for converting text strings to a form that is suitable for use as part of an URL. This format is known as x-www-form-urlencoded and is typically used to encode form data that is sent to a CGI script.
The encode() method converts spaces to plus signs (+) and uses the percent character (%) as an escape code to encode special characters. The two characters that immediately follow a percent sign are interpreted as hexadecimal digits that are combined to produce an eight-bit value.
Listing 30.7 illustrates the use of the encode() method and the URLConnection class. The QueryURLApp program accesses the echo-query CGI program on my Web server, passing it the "/this/is/extra/path/information" query string and the "Query string with some special characters: @#$%?&+" query string. The query string is encoded using the encode() method of the URLEncoder class. The echo-query CGI program creates an HTML file that describes the parameters passed to it by my Web server and returns this file to the QueryURLApp program. This file shows how the query string was encoded by the encode() method.
import java.lang.System;
import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.net.MalformedURLException; import java.net.UnknownServiceException; import java.io.*; public class QueryURLApp { public static void main(String args[]){ try{ String urlString = "http://www.jaworski.com/cgi-bin/echo-query"; String extraPathInfo = "/this/is/extra/path/information"; String queryString = URLEncoder.encode("Query string with some special characters:"+ Â" @#$%?&+"); URL url = new URL(urlString+extraPathInfo+"?"+queryString); URLConnection connection = url.openConnection(); BufferedReader fromURL = new BufferedReader( new InputStreamReader(url.openStream())); String line; while ((line = fromURL.readLine())!= null){ System.out.println(line); } fromURL.close(); }catch (MalformedURLException ex){ error("Bad URL"); }catch (UnknownServiceException ex){ error("UnknownServiceException occurred."); }catch (IOException ex){ error("IOException occurred."); } } public static void error(String s){ System.out.println(s); System.exit(1); }
To run QueryURLApp, just type the following command line:
java QueryURLApp
QueryURLApp queries the echo-query program on my Web server and displays the HTML file generated by the echo-query program. Notice how the query string was encoded:
<HTML>
<HEAD> <TITLE>Echo CGI Request</TITLE> </HEAD> <BODY> <H1>CGI Request</H1> <H2>Command Line Arguments</H2> <P>Number of command line arguments: 7</P> <P>Command line arguments: Query string with some special characters: Â @#\$%\?\&+ </P> <H2>Environment Variables</H2> <PRE> AUTH_TYPE = CONTENT_LENGTH = CONTENT_TYPE = GATEWAY_INTERFACE = CGI/1.1 HTTP_ACCEPT = text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 HTTP_USER_AGENT = Javainternal_build PATH_INFO = /this/is/extra/path/information PATH_TRANSLATED = /usr/local/etc/httpd/htdocs/this/is/extra/path/ Â information QUERY_STRING = Â Query+string+with+some+special+characters%3a+ Â %40%23%24%25%3f%26%2b REMOTE_ADDR = 204.212.153.194 REMOTE_HOST = athome.jaworski.com REMOTE_IDENT = REMOTE_USER = REQUEST_METHOD = GET SCRIPT_NAME = /cgi-bin/echo-query SERVER_NAME = www.jaworski.com SERVER_PORT = 80 SERVER_PROTOCOL = HTTP/1.0 SERVER_SOFTWARE = NCSA/1.4.2 </PRE> <H2>Standard Input</H2> </BODY> </HTML>
QueryURLApp creates an URL by concatenating the URL for the echo-query program, the extra path information, and the encoded query string. It then uses the openConnection() method of the URL class to create an URLConnection object, which it assigns to the connection variable. The connection is then read and displayed in the same manner as the GetURLApp program.
The ContentHandler class is an abstract class that is used to develop specialized objects that can extract and process data associated with new MIME types.
MIME, or multipurpose Internet mail extension, is a general method by which the content of different types of Internet objects can be identified. MIME was originally developed to include different types of objects, such as sounds, images, and videos, in Internet email messages. It was also adopted and popularized by the Web and is used to identify multimedia and other types of Web objects so that appropriate external viewers or plug-in modules can be used to process and display these objects.
The ContentHandler class provides the basis for developing new viewers for processing MIME types that are not currently supported by Java. It consists of a single method, getContent(), that extracts an object of a particular MIME type from an URL connection.
The ContentHandlerFactory interface provides a standard method of associating a content handler with a MIME type. The FileNameMap interface provides the capability to associate a filename with a MIME type.
Chapter 33, "Content and Protocol Handlers," provides a detailed description of how content handlers are developed.
The URLStreamHandler class is an abstract class that is used to develop specialized objects that can communicate with Web resources using protocols that are currently not supported by Java. For example, suppose you develop a new protocol for a custom client/server application and you want that protocol to be accessible to Web browsers. You would develop an URLStreamHandler for that protocol. The URLStreamHandlerFactory interface is used to associate a stream handler with a particular protocol. Chapter 33 also covers protocol stream handlers.
In this chapter you learned about Java's support of network programming and about the basics of client/server computing and TCP/IP socket programming. You toured the classes of the java.net package and learned how to use them to develop client/server applications. You also learned about the URL-centric classes that support Web-based applications. The next chapter shows you how to apply what you've learned to develop Internet client software.
© Copyright, Macmillan Computer Publishing. All rights reserved.