Implementing Servers


Now that we have implemented a basic network client that receives data from the Internet, let's implement a simple server that can send information to clients. Once you start the server program, it waits for some client to attach to its port. We chose port number 8189, which is not used by any of the standard services. The ServerSocket class establishes a socket. In our case, the command

 ServerSocket s = new ServerSocket(8189); 

establishes a server that monitors port 8189. The command

 Socket incoming = s.accept(); 

tells the program to wait indefinitely until a client connects to that port. Once someone connects to this port by sending the correct request over the network, this method returns a Socket object that represents the connection that was made. You can use this object to get input and output streams, as is shown in the following code:

 InputStream inStream = incoming.getInputStream(); OutputStream outStream = incoming.getOutputStream(); 

Everything that the server sends to the server output stream becomes the input of the client program, and all the output from the client program ends up in the server input stream.

In all the examples in this chapter, we will transmit text through sockets. We therefore turn the streams into scanners and writers.

 Scanner in = new Scanner(inStream); PrintWriter out = new PrintWriter(outStream, true /* autoFlush */); 

Let's send the client a greeting:

 out.println("Hello! Enter BYE to exit."); 

When you use telnet to connect to this server program at port 8189, you will see the preceding greeting on the terminal screen.

In this simple server, we just read the client input, a line at a time, and echo it. This demonstrates that the program receives the client's input. An actual server would obviously compute and return an answer that depended on the input.

 String line = in.nextLine(); out.println("Echo: " + line); if (line.trim().equals("BYE")) done = true; 

In the end, we close the incoming socket.

 incoming.close(); 

That is all there is to it. Every server program, such as an HTTP web server, continues performing this loop:

  1. It receives a command from the client ("get me this information") through an incoming data stream.

  2. It somehow fetches the information.

  3. It sends the information to the client through the outgoing data stream.

Example 3-2 is the complete program.

Example 3-2. EchoServer.java
  1. import java.io.*;  2. import java.net.*;  3. import java.util.*;  4.  5. /**  6.    This program implements a simple server that listens to port 8189 and echoes back all  7.    client input.  8. */  9. public class EchoServer 10. { 11.    public static void main(String[] args ) 12.    { 13.       try 14.       { 15.          // establish server socket 16.          ServerSocket s = new ServerSocket(8189); 17. 18.          // wait for client connection 19.          Socket incoming = s.accept( ); 20.          try 21.          { 22.             InputStream inStream = incoming.getInputStream(); 23.             OutputStream outStream = incoming.getOutputStream(); 24. 25.             Scanner in = new Scanner(inStream); 26.             PrintWriter out = new PrintWriter(outStream, true /* autoFlush */); 27. 28.             out.println( "Hello! Enter BYE to exit." ); 29. 30.             // echo client input 31.             boolean done = false; 32.             while (!done && in.hasNextLine()) 33.             { 34.                String line = in.nextLine(); 35.                out.println("Echo: " + line); 36.                if (line.trim().equals("BYE")) 37.                   done = true; 38.             } 39.          } 40.          finally 41.          { 42.             incoming.close(); 43.          } 44.       } 45.       catch (IOException e) 46.       { 47.          e.printStackTrace(); 48.       } 49.    } 50. } 

To try it out, compile and run the program. Then, use telnet to connect to the following server and port:

Server: 127.0.0.1

Port: 8189

The IP address 127.0.0.1 is a special address, the local loopback address, that denotes the local machine. Because you are running the echo server locally, that is where you want to connect.

If you are connected directly to the Internet, then anyone in the world can access your echo server, provided they know your IP address and the magic port number.

When you connect to the port, you will see the message shown in Figure 3-4:

 Hello! Enter BYE to exit. 

Figure 3-4. Accessing an echo server


Type anything and watch the input echo on your screen. Type BYE (all uppercase letters) to disconnect. The server program will terminate as well.


 java.net.ServerSocket 1.0 

  • ServerSocket(int port)

    creates a server socket that monitors a port.

  • Socket accept()

    waits for a connection. This method blocks (that is, idles) the current thread until the connection is made. The method returns a Socket object through which the program can communicate with the connecting client.

  • void close()

    closes the server socket.

Serving Multiple Clients

There is one problem with the simple server in the preceding example. Suppose we want to allow multiple clients to connect to our server at the same time. Typically, a server runs constantly on a server computer, and clients from all over the Internet may want to use the server at the same time. Rejecting multiple connections allows any one client to monopolize the service by connecting to it for a long time. We can do much better through the magic of threads.

Every time we know the program has established a new socket connection, that is, when the call to accept was successful, we will launch a new thread to take care of the connection between the server and that client. The main program will just go back and wait for the next connection. For this to happen, the main loop of the server should look like this:

 while (true) {    Socket incoming = s.accept();    Runnable r = new ThreadedEchoHandler(incoming);    Thread t = new Thread(r);    t.start(); } 

The THReadedEchoHandler class implements Runnable and contains the communication loop with the client in its run method.


class ThreadedEchoHandler implements Runnable
{ . . .
   public void run()
   {
      try
      {
         InputStream inStream = incoming.getInputStream();
         OutputStream outStream = incoming.getOutputStream();
         process input and send response
         incoming.close();
      }
      catch(IOException e)
      {
         handle exception
      }
   }
}

Because each connection starts a new thread, multiple clients can connect to the server at the same time. You can easily check this out. Compile and run the server program (Example 3-3). Open several telnet windows as we have in Figure 3-5. You can communicate through all of them simultaneously. The server program never dies. Use CTRL+C to kill it.

Figure 3-5. Several telnet windows communicating simultaneously


NOTE

In this program, we spawn a separate thread for each connection. This approach is not satisfactory for high-performance servers. You can achieve greater server throughput by using features of the java.nio package. See http://www-106.ibm.com/developerworks/java/library/j-javaio for more information.


Example 3-3. ThreadedEchoServer.java

[View full width]

  1. import java.io.*;  2. import java.net.*;  3. import java.util.*;  4.  5. /**  6.    This program implements a multithreaded server that listens to port 8189 and echoes  back  7.    all client input.  8. */  9. public class ThreadedEchoServer 10. { 11.    public static void main(String[] args ) 12.    { 13.       try 14.       { 15.          int i = 1; 16.          ServerSocket s = new ServerSocket(8189); 17. 18.          while (true) 19.          { 20.             Socket incoming = s.accept(); 21.             System.out.println("Spawning " + i); 22.             Runnable r = new ThreadedEchoHandler(incoming, i); 23.             Thread t = new Thread(r); 24.             t.start(); 25.             i++; 26.          } 27.       } 28.       catch (IOException e) 29.       { 30.          e.printStackTrace(); 31.       } 32.    } 33. } 34. 35. /** 36.    This class handles the client input for one server socket connection. 37. */ 38. class ThreadedEchoHandler implements Runnable 39. { 40.    /** 41.       Constructs a handler. 42.       @param i the incoming socket 43.       @param c the counter for the handlers (used in prompts) 44.    */ 45.    public ThreadedEchoHandler(Socket i, int c) 46.    { 47.       incoming = i; counter = c; 48.    } 49. 50.    public void run() 51.    { 52.       try 53.       { 54.          try 55.          { 56.             InputStream inStream = incoming.getInputStream(); 57.             OutputStream outStream = incoming.getOutputStream(); 58. 59.             Scanner in = new Scanner(inStream); 60.             PrintWriter out = new PrintWriter(outStream, true /* autoFlush */); 61. 62.             out.println( "Hello! Enter BYE to exit." ); 63. 64.             // echo client input 65.             boolean done = false; 66.             while (!done && in.hasNextLine()) 67.             { 68.                String line = in.nextLine(); 69.                out.println("Echo: " + line); 70.                if (line.trim().equals("BYE")) 71.                   done = true; 72.             } 73.          } 74.          finally 75.          { 76.             incoming.close(); 77.          } 78.       } 79.       catch (IOException e) 80.       { 81.          e.printStackTrace(); 82.       } 83.    } 84. 85.    private Socket incoming; 86.    private int counter; 87. } 



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

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