Now that you have seen how sockets work on the Internet via high-level classes such as URL , you'll see how sockets work when used directly. A socket is one endpoint of a network connection that has a port number. One way to think about sockets is to use an analogy. Sockets are like apartment complexes. To make a proper socket connection you need an IP address and port number. To deliver a letter to an apartment, you need the street address (analogous to IP address) and the apartment number (analogous to the port number). With that analogy in mind, you open a socket like so: String apartmentAddress = "123 main street"; int apartmentNumber = 4; Socket apartment = new Socket(apartmentAddress, apartmentNumber); The apartmentAddress is the equivalent to the IP address; the address you are trying to open a connection to. The apartmentNumber is equivalent to the port the server is listening to. Regarding port numbers, if you are writing your own server and running it on a production network, I recommend using port numbers from 49152 through 65535 to avoid using a number registered by another application. If you are just testing on a small network or are running only on a local machine, you need to use a number above 1,023 because ports 1,023 and below are considered reserved for protocols such as FTP, HTTP, Gopher, and the like. The Internet Assigned Numbers Authority (IANA) manages the port-protocol assignments necessary for operating network applications and the Internet. IANA has structured port numbers so that they are divided into three ranges: the Well Known Ports, the Registered Ports, and the Dynamic and/or Private Ports (http://www.iana.org/assignments/port- numbers ). The following list describes the three port number ranges:
The Internet uses some protocols that are asynchronous and some that are synchronous. For example, FTP is functionally synchronous, and HTTP is asynchronous. Asynchronous communication is more reliable and efficient. To better understand this concept, compare HTTP over the Internet with how telephone networks have traditionally worked. Efficiency in telephone networks is terrible because the connections are synchronous. When you call your mom, your phone is connected directly and stays that way until one of you disconnects. Think about it: A single connection (one strand of hundreds of wires and switches linked together) is dedicated to your phone call. Even when nobody is talking, the line is yours and no one else can share it. Now compare this to how HTTP works on the Internet. When you surf to a Web site, you get one page (that is, a file) at a time. Basically, your browser sets up a socket and uses it to send a request on the wire, and the Internet takes it from there. Your browser listens and waits, but is not connected to, for example, Amazon's Web server. The Internet routes your request to Amazon's Web farm. Eventually, one of the servers is contacted, parses the request, and puts the requested file (Web servers are just file-copy applications) on the wire, but is not connected to your machine. The Internet routes the file's bytes back to the requesting machine. Your browser gets the file through its socket and saves to its disk. This process is entirely asynchronous and demonstrates efficient use of Internet hardware, a very different approach to communication than what telephone networks use. The most powerful thing you can do with sockets is build a server. The server in this next example fundamentally provides a service in the same way that database, Web, and FTP servers do. First, you initialize the ServerSocket object, as shown here: ServerSocket serverSocket = new ServerSocket(PortNumber); Inside the ServerSocket class is a mini-application that hides a lot of socket details you don't have to worry about. This mini-application starts a thread on your computer that sets the foundation for this line: Socket clientSocket = serverSocket.accept(); The ServerSocket.accept() method waits for (or is said to be "listening for") a client request. The clientSocket has an endless loop that polls the system for a request to show up on the PortNumber port. These two lines are the heart of a real server. You need just a few minor additions to make this server useful, but you have already gotten close to having a functioning server. The socket is listening to the port, and you don't yet have a way to handle messages that might come your way. The following code adds message handling: BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream())); The Socket.getInputStream() method is the socket's method for getting a handle on a request in the form of an input stream (sequence of bytes) for this socket. The second line creates a Reader that enables you to grab bytes off the stream. The first line just gets bytes in bunches for efficiency, but it isn't necessary. You need one last item to complete the server. The following code is how the server obtains a PrintWriter for printing text back to the client in response to the client's request: PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
The preceding code snippets give you all the pieces to build a server. The following program is a functioning server, although it doesn't do much but send gibberish to the client. The client does get to quit the connection with the key phrase yes dear . Based on a little marriage humor, Listing 11.7 shows you how to build a server using sockets. In this example, the WifeServer class demonstrates how to set up a socket to listen for client requests . Java makes it easy to create a server by providing socket classes that do a lot of the detail work, leaving only simple methods for developers to worry about. Listings 11.7 and 11.8 work together. After compiling both, you use them by first starting WifeServer (that is, java WifeServer ). Then run HusbandClient (that is, java HusbandClient ). After the HusbandClient program is running, you can type messages on the command line and have answers from WifeServer displayed in the HusbandClient command window. With both of them running, type yes dear in the HusbandClient command line and press Enter. When you do so, HusbandClient sends your text as a request to WifeServer . After the connection is made, WifeServer processes the request. In this case, WifeServer merely compares the text of your request against a small list of texts stored in WifeServer . If you typed yes dear in the HusbandClient command line and pressed Enter, WifeServer responds with I love you, dear . In the HusbandClient window, you see the text wife says: I love you, dear . Although the husband/wife analogy is simple, the interaction between two programs via sockets is a powerful capability that Java makes easy to build. Now take a look at the first part of the duo, Listing 11.7. Listing 11.7 Building a Server with Socketsimport java.net.*; import java.io.*; import java.util.Random; //this class creates a server socket //that listens on a port for client requests public class WifeServer { public static void main(String[] args) throws IOException { int PortNumber = 5108; //her ATM password ServerSocket wifeSocket = null; try { //create a server socket that listens on port 5108 wifeSocket = new ServerSocket(PortNumber); } catch (IOException e) { System.err.println("Port " + PortNumber + " not available." ); System.exit(1); } Socket husbandSocket = null; try { //this simple statement creates the socket //that listens for client requests. husbandSocket = wifeSocket.accept(); } catch (IOException e) { System.err.println("Port " + PortNumber + " failure."); System.exit(1); } try { PrintWriter out = new PrintWriter(husbandSocket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader( husbandSocket.getInputStream())); String husbandSays, wifeSays; Wife wife = new Wife(); wifeSays = wife.answer(null); out.println(wifeSays); //get the request text while ((husbandSays = in.readLine()) != null) { wifeSays = wife.answer(husbandSays); out.println(wifeSays); if (wife.turnOff) { break; } } out.close(); in.close(); husbandSocket.close(); } catch (IOException e) { System.out.println(e); } wifeSocket.close(); } } //this class parses the request //and responds accordingly. class Wife { public boolean turnOff = false; private String answer = ""; private String[] sweetNothings = { "Take out that smelly garbage.", "Mow the lawn you bum.", "Fix the sink you broke.", "We talked about this already.", "No! My birthday was last Monday." }; public String answer(String husbandSays) { String theOutput = null; if (husbandSays == null) { this.answer = "I love you, dear."; } else if (husbandSays.indexOf("yes dear")!=-1) { this.answer = "I still love you, honey."; } else { Random random = new Random(); int whisper = random.nextInt(5); this.answer = this.sweetNothings[whisper]; } return this.answer; } } The solution shown in Listing 11.7 represents a server. Now you need a client, as shown in Listing 11.8, to talk to the server. This client creates a request by connecting to the server socket on a predetermined port. After the connection is made, the request is sent to the server. Listing 11.8 Building a Client with Socketsimport java.io.*; import java.net.*; //this class is a client that connects //to a server listening to a predetermined port. public class HusbandClient { public static void main(String[] args) throws IOException { Socket husbandSocket = null; PrintWriter out = null; BufferedReader wifeServer = null; String machineName = "localhost", wifeSays = null; int portNumber = 5108; BufferedReader in = null; try { husbandSocket = new Socket(machineName, portNumber); out = new PrintWriter(husbandSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader( husbandSocket.getInputStream())); } catch (UnknownHostException e) { System.err.println("Failure-" + machineName + ":" + portNumber); System.exit(1); } catch (IOException e) { System.err.println("Bad I/O-" + machineName + ":" + portNumber); System.exit(1); } try { wifeServer = new BufferedReader( new InputStreamReader(System.in)); while ((wifeSays = wifeServer.readLine()) != null) { out.println(wifeSays); System.out.println("wife says: " + in.readLine()); } } catch (IOException e) { out.close(); in.close(); wifeServer.close(); husbandSocket.close(); } } }
You start the server first and then the client. The program will not work the other way because the server is listening for the client. If the client is started first, it will fail because it tries to connect to the server, which doesn't exist. To see output, bring the window in which the HusbandClient is running to the foreground. Type a message and press Enter, or just press Enter by itself. Try this a few times and observe the results. At some point, enter "yes dear" and notice that the WifeServer gives a kind feedback message rather than a commanding one. Not only does this program show the general structure of building socket applications should you chose sockets over RMI for the certification assignment, but it also shows how to build a client and server that communicate with one another in an easy-to-understand example. Figure 11.1. Illustrating socket programming with a server/client application.
Good-natured ribbing aside, notice that you need two command prompts to make this program work: one for the server and one for the client. After the server starts ( java WifeServer ), it waits for someone to knock on its port, which is set at 5108. The client starts by calling on the same machine and knocking on port 5108. The server accepts the connection and sends the first message to the client ( wife says: I love you, dear ). From the client console, you send messages to the server, and the server responds with randomly selected sentences. When you are done using the application, press Ctrl+C in each window to quit that part of the application. This client/server pair is simple, but it shows you all the steps for setting up a basic socket-based server and building a rudimentary client. The client socket and server socket do the same things: listen for each other, receive messages, process messages, and, finally, respond. The difference between the two sockets is that the client visits first, and the server waits patiently for company. This set of steps is the general process a socket follows , whether it's on the client or server:
|