Programming Simple Sockets

   

Java™ 2 Primer Plus
By Steven Haines, Steve Potts

Table of Contents
Chapter 20.  Network Programming


The Socket and SocketServer classes are the most frequently used classes in the package. A socket is really just a numbered connection on a computer. This number is called the socket's port. If the sending and receiving computers agree on what port they want to use, they can communicate. The TCP/IP software on one computer sends messages containing the port number to the other computer. The receiving computer's TCP/IP software receives the message and checks to see if any program that it knows of is accepting messages on this port. If the answer is yes, it hands the message to that program.

For a program to work, you need to have both a client and a server. Listing 20.1 shows the code for a socket server.

Listing 20.1 The SimpleSocketClient.java File
 *   * SimpleSocketClient.java   *   * Created on October 2, 2002, 5:34 PM   */  package ch20;  import java.net.*;  import java.io.*;  /**   *   * @author  Stephen Potts   */  public class SimpleSocketClient  {      Socket socket;      int portNumber = 1777;      String str = "";      /** Creates a new instance of SimpleSocketClient */      public SimpleSocketClient()      {          try          {              socket = new Socket(InetAddress.getLocalHost(),              portNumber);              ObjectInputStream ois =              new ObjectInputStream(socket.getInputStream());              str = (String) ois.readObject();              System.out.println(str);          } catch (Exception e)          {              System.out.println("Exception " + e);          }      }      public static void main(String args[])      {          SimpleSocketClient ssp = new SimpleSocketClient();      }  } 

We import the java.net package because it contains the Socket class. The java.io package provides input and output classes that we will use to extract the data from the response.

 import java.net.*;  import java.io.*; 

We can choose just about any port number that we want, but some of them might already be in use. If you have a Web server running on your machine, it is likely set to use port 80, so you wouldn't want to use that number.

 int portNumber = 1777; 

We instantiate the socket using the address of this computer and the port number.

 socket = new Socket(InetAddress.getLocalHost(),  portNumber); 

The program will try to open this socket on this machine. If there is no server running on that port, an Exception will be thrown. The getInputStream() method obtains a handle to the socket's stream.

 ObjectInputStream ois =  new ObjectInputStream(socket.getInputStream()); 

The readObject() method reads from the stream and places it in the str variable.

 str = (String) ois.readObject(); 

Finally, we print the contents of the str variable so that we can see what was sent.

 System.out.println(str); 

Unlike other programs, TCP programs are useless unless there is a corresponding server to connect to. The server uses the same port number as the client.

Listing 20.2 shows a server that communicates with the client in Listing 20.3:

Listing 20.2 The SimpleSocketServer.java File
 /*   * SimpleSocketServer.java   *   * Created on October 2, 2002, 5:24 PM   */  package ch20;  import java.net.*;  import java.io.*;  /**   *   * @author  Stephen Potts   */  public class SimpleSocketServer  {      ServerSocket serverSocket;       int portNumber = 1777;      Socket socket;      String str;      /** Creates a new instance of SimpleSocketServer */      public SimpleSocketServer()      {        str = " <?xml version=\"1.0\" encoding=\"UTF-8\"?>";               str += "<ticketRequest><customer custID=\"2030\">";               str += "<lastName>Smith</lastName>";               str += "<firstName>Klay</firstName>";               str += "</customer>";               str += "<cruise cruiseID=\"3009\">";               str += "<destination>Caribbean</destination>";               str += "<port>San Juan</port>";               str += "<sailing>4/30/02</sailing>";               str += "<numberOfTickets>5</numberOfTickets>";               str += "</cruise>";               str += "</ticketRequest>";               // Create ServerSocket to listen for connections               try               {                   serverSocket = new ServerSocket(portNumber);                   // Wait for client to connnect, then get Socket                   System.out.println("ServerSocket created");                   System.out.println("Waiting for a connection on " +                                                          portNumber);                    socket = serverSocket.accept();                   // Use ObjectOutputStream to send String to the client                   ObjectOutputStream oos =                   new ObjectOutputStream(socket.getOutputStream());                   oos.writeObject(str);                   oos.close();                   // Close Socket                   socket.close();               } catch (Exception e)               {                   System.out.println("Exception " + e);                }      }      public static void main(String args[])      {          SimpleSocketServer sss = new SimpleSocketServer();      }  } 

Instead of declaring a Socket, we declare an instance of a ServerSocket. ServerSockets are sockets that are able to wait for a communication from anther process or computer. Normal sockets throw an error if they can't find their counterpart.

 ServerSocket serverSocket; 

Notice that we use the same port number as the client program did.

 int portNumber = 1777; 

We build up a String with the data that we want to send.

 str = " <?xml version=\"1.0\" encoding=\"UTF-8\"?>";  str += "<ticketRequest><customer custID=\"2030\">";   .   .   .  str += "</cruise>";  str += "</ticketRequest>"; 

The ServerSocket passes a port number in its constructor.

 serverSocket = new ServerSocket(portNumber); 

We print out some messages so that we can obtain a visual confirmation that the server has started up.

 System.out.println("ServerSocket created");  System.out.println("Waiting for a connection on " +                                         portNumber); 

The accept() method tells the TCP/IP software that this program would like to wait until a request comes in on the previously specified port, and to accept the connection when it is requested.

 socket = serverSocket.accept(); 

We use an ObjectOutputStream to send the data out. In this case, we always send the same message regardless of which process connects.

 ObjectOutputStream oos =  new ObjectOutputStream(socket.getOutputStream()); 

We write the String out using the stream handle that was obtained from the socket.

 oos.writeObject(str); 

We close the stream now that we are done with it.

 oos.close(); 

We also close the socket.

 socket.close(); 

The result is a string that contains an XML document as shown here.

 <?xml version="1.0" encoding="UTF-8"?><ticketRequest>  <customer cust><lastName>Carter</lastName>  <firstName>Joey</firstName></customer><cruise cruise>  <destination>Alaska</destination><port>Vancouver</port>  <sailing>4/15/02</sailing><numberOfTickets>4</numberOfTickets>  </cruise></ticketRequest> 

Sending XML documents over sockets is a very convenient way to transfer complex data between computers. This is especially true when the two computers are running different operating systems because every operating system can run TCP/IP.

Two-Way Communication Using Sockets

There is no reason that the communication between the client and the server can't be two-way. In the preceding example, both programs ended as soon as they processed one message. You can modify the program to accept a message and respond to it just as easily. Listing 20.3 shows a client that stays alive until the client indicates that he wants to end it.

Listing 20.3 The LoopingSocketClient.java File
 /*   * LoopingSocketClient.java   *   * Created on October 2, 2002, 5:34 PM   */  package ch20;  import java.net.*;  import java.io.*;  /**   *   * @author  Stephen Potts   */  public class LoopingSocketClient  {      Socket socket1;      int portNumber = 1777;      String str = "";      /** Creates a new instance of LoopingSocketClient */      public LoopingSocketClient()      {          try          {              socket1 = new Socket(InetAddress.getLocalHost(),              portNumber);              ObjectInputStream ois =              new ObjectInputStream(socket1.getInputStream());              ObjectOutputStream oos =               new ObjectOutputStream(socket1.getOutputStream());              str = "initialize";              oos.writeObject(str);              while ((str = (String) ois.readObject()) != null)              {                  System.out.println(str);                  oos.writeObject("bye");                  if (str.equals("bye bye"))                      break;               }              ois.close();              oos.close();              socket1.close();          } catch (Exception e)          {              System.out.println("Exception " + e);          }      }      public static void main(String args[])      {          LoopingSocketClient lsp = new LoopingSocketClient();      }  } 

We first instantiate a single socket class by giving it the name of a computer and the port number that we want to use. This example uses the same computer to run the client and the server.

 socket1 = new Socket(InetAddress.getLocalHost(),  portNumber); 

If you replace the call to getLocalHost() with a string such as "Pogo," this example would attempt to contact a SocketServer object running on a machine known as "Pogo" on your local network:

 socket1 = new Socket("localhost",  portNumber); 

You could also use the IP address such as "123.35.67.8" of the machine instead:

 socket1 = new Socket("127.0.0.1",  portNumber); 

Most programmers prefer to use the name because IP addresses can change from time to time.

Tip

graphics/01icon19.gif

Java provides five ways to address your own machine from within a program. The first way is to call the static method getLocalHost() in the InetAddress class. The second way is to use the String "127.0.0.1", which always means this machine. The third way is to put the real IP address of your machine in a String as in "123.35.67.8"; this only works if you have a static IP address. The fourth way is to use the String "localhost" where the name would be placed. The fifth way is to enter the actual name of your machine on the local area network as in "Speedracer."


There are several different objects that you can use to put data onto a socket and get data off of one. In this example, we used the ObjectInputStream and the ObjectOutputStream. The ObjectOutputStream class can only be used with Objects that are serializable. Serializable classes are those that can be written to a file or socket and reconstituted on another computer properly. In our example, we use String objects to pass the data. Because the String class is serializable, this works.

 ObjectInputStream ois =              new ObjectInputStream(socket1.getInputStream()); 

The ObjectOutputStream can be used to place objects on the stream. This class is not only associated with sockets, but it can be used for files as well. For that reason, we have to use the getOutputStream() class to get an object that it can accept.

 ObjectOutputStream oos =  new ObjectOutputStream(socket1.getOutputStream()); 

When we start the client, we want to send some sort of an initialization string to make sure that we are up and running. If there is no ServerSocket listening on the specified port, an Exception will be thrown.

 str = "initialize"; 

The actual writing of the String object is accomplished by calling the writeObject() method.

 oos.writeObject(str); 

We don't want this program to terminate after only one pass through it, so we create a while loop that will perform our read also. Notice that the readOject() method needs an explicit cast to type String. This method returns an object of type Object. The cast turns it into a String. There are restrictions on what kind of cast is allowed, but you are safe here because both programs are only using Strings to pass the data.

 while ((str = (String) ois.readObject()) != null)  {      System.out.println(str); 

In this example, we send the initialization string first. The next string that we send is the "bye" string. This indicates to our program that the client wants the server to shutdown.

 oos.writeObject("bye"); 

The message that the server sends when it is shutting down is a similar string that contains the words "bye bye". When the program sees this, it issues a break command to exit the loop.

 if (str.equals("bye bye"))      break; 

When the process ends, all the streams and sockets will be closed automatically. It is considered good form to close them yourself, however, because explicit calls are easier for the JVM to deal with. In some situations, this might improve performance slightly.

 ois.close();  oos.close();  socket1.close(); 

The other half of the program is the server. The server in a two-way conversation is not much different from the client. It too uses the socket to create both an ObjectInputStream and an ObjectOutputStream. The main difference is that the server contains an instance of the ServerSocket. This special class waits for another program to contact it. Listing 20.4 shows a socket server that communicates both ways.

Listing 20.4 The LoopingSocketServer.java File
 /*   * LoopingSocketServer.java   *   * Created on October 2, 2002, 5:24 PM   */  package ch20;  import java.net.*;  import java.io.*;  /**   *   * @author  Stephen Potts   */  public class LoopingSocketServer  {      ServerSocket servSocket;      Socket fromClientSocket;      int cTosPortNumber = 1777;      String str;      /** Creates a new instance of LoopingSocketServer */      public LoopingSocketServer()      {          // Create ServerSocket to listen for connections          try          {              servSocket = new ServerSocket(cTosPortNumber);               // Wait for client to connnect, then get Socket              System.out.println("ServerSocket created");              System.out.println("Waiting for a connection on " +              cTosPortNumber);              fromClientSocket = servSocket.accept();              System.out.println("fromClientSocket accepted");              // Use ObjectOutputStream to send String to the client              ObjectOutputStream oos =              new ObjectOutputStream(fromClientSocket.getOutputStream());              //Use ObjectInputStream to get String from client              ObjectInputStream ois =              new ObjectInputStream(fromClientSocket.getInputStream());              while ((str = (String) ois.readObject()) != null)              {                  System.out.println("The message from client is *** " +                                                                    str);                  if (str.equals("bye"))                  {                      oos.writeObject("bye bye");                      break;                  }                  else                  {                      str = "Server returns " + str;                      oos.writeObject(str);                  }              }              oos.close();              // Close Sockets              fromClientSocket.close();          } catch (Exception e)          {              System.out.println("Exception " + e);          }      }      public static void main(String args[])      {          LoopingSocketServer lss = new LoopingSocketServer();      }  } 

The primary difference between the server and the client is that the server contains a ServerSocket object, which is used to make the connection to the client.

 servSocket = new ServerSocket(cTosPortNumber); 

The accept command will provide us with a handle to the socket that the client requested. If no client is available when the server is started, the server just waits for it. It does not throw an Exception the way the client does.

 fromClientSocket = servSocket.accept();  System.out.println("fromClientSocket accepted"); 

We instantiate an ObjectOutputStream object so that we can send data to the ObjectInputStream on the client.

 ObjectOutputStream oos =  new ObjectOutputStream(fromClientSocket.getOutputStream());  //Use ObjectInputStream to get String from client  ObjectInputStream ois =  new ObjectInputStream(fromClientSocket.getInputStream()); 

We also use a while loop on the server to keep this program alive until we are ready to shut it down.

 while ((str = (String) ois.readObject()) != null)  { 

We print the message to standard output.

 System.out.println("The message from client is *** " +                                                    str); 

We test for the "bye" message first. When we receive it, we send the "bye bye" message, then break.

 if (str.equals("bye"))  {      oos.writeObject("bye bye");      break;  } 

Otherwise, this is a valid message that needs to be processed.

 else  {      str = "Server returns " + str; 

Writing to the server's ObjectOutputStream will send data to the ObjectInputStream of the client.

 oos.writeObject(str); 

You must start the server program first. These two programs interact with each other until the "bye" command is sent. The results of running these program are shown here:

The server output looks like this:

 ServerSocket created  Waiting for a connection on 1777  fromClientSocket accepted  The message from client is *** initialize  The message from client is *** bye 

The client output looks like this:

 Server returns initialize  bye bye 
Figure 20.4. The Socket communication process.

graphics/20fig04.gif

The following sequence of events takes place in this diagram.

  1. The ServerSocket object is created on the server. The created and waiting messages are issued.

  2. The ServerSocket object calls the accept() method.

  3. The client opens a socket and writes the word "initialize" to it.

  4. The accept() method returns the socket handle.

  5. The server uses the socket handle to call readObject().

  6. The server prints the information to output and calls writeObject(), which contains a String that says "Server returns initialize."

  7. The client makes a call to readObject() and retrieves the String object.

  8. The client prints the "Server returns initialize" string.

  9. The client calls writeObject(), and sends the string "bye."

  10. The server calls readObject() and gets the String "bye."

  11. The server prints out the String "bye."

  12. The server calls writeObject() and sends the string "bye bye."

  13. The server breaks out of the loop, closes the streams and socket, then exits.

  14. The client calls readObject() to get the string "bye bye."

  15. The client prints the string "bye bye."

  16. The client breaks out of the loop, closes the streams and socket, then exits.

Don't be surprised if it takes a while to understand this example. It contains some processing that is not trivial to understand.


       
    Top
     



    Java 2 Primer Plus
    Java 2 Primer Plus
    ISBN: 0672324156
    EAN: 2147483647
    Year: 2001
    Pages: 332

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