Working with Sockets


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:

  • Well Known Ports Ports 0 through 1023 are used by well-known protocols, such as 80=HTTP, 21=FTP, and 23=Telnet, so don't use a port in this range for a custom protocol.

  • Registered Ports Ports from 1024 through 49151 are used by vendors such as Cisco. For example, Oracle uses 1525, and Microsoft uses 3074 for its Xbox game port. I recommend not using these ports for production code, but they are fine for testing.

  • Dynamic and/or Private Ports These ports are not reserved, so you can safely use them without worrying about problems with another application from a well-known vendor. You could still have problems, however, if another application is using the same port, even if that port is not registered with IANA.

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); 
graphics/tip_icon.gif

Use PrintWriter to send text data, but PrintStream to send binary. Sun's documentation says to use PrintStream to send character data as bytes, but do not use it for non-character data. Also, the PrintWriter methods never throw an IOException , so use the PrintWriter's checkError() instead.


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 Sockets
 import 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 Sockets
 import 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();       }    } } 
graphics/tip_icon.gif

The client and server write to and read from their respective sockets. You must have four try-catch blocks to make this code work properly, one for each direction of each socket.


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.

graphics/11fig01.jpg

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:

  1. Open a connection consisting of a socket with a domain and a port.

  2. Open an input stream that collects data from the socket.

  3. Read from the stream and assign the data read to variables as desired.

  4. Implement a decision tree to take action based on the received bytes.

  5. Open an output stream that sends data to the socket.

  6. Write output to the stream and assign bytes to the stream from variables.

  7. Close the stream and send resources to the garbage collector.



JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
ISBN: N/A
EAN: N/A
Year: 2003
Pages: 187

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net