| 
	|  |      The Developer's Resource & Community Site
 |    Java Network Programming, 2nd Edition: Sockets for Servers Reproduced with kind permision of O'Reilly & Associates: www.oreilly.com Contents:Sockets for 
  Servers
 In this chapter: The 
  ServerSocket Class
 Some 
  Useful Servers
 The last chapter discussed sockets from the standpoint of 
  clients: programs that open a Socket to a server that's listening for 
  connections. However, client sockets themselves aren't enough; clients aren't 
  much use unless they can talk to a server, and if you think about it, the 
  sockets we discussed in the last chapter aren't sufficient for writing 
  servers. To create a Socket, you need to know the 
  Internet host to which you want to connect. When you're writing a server, you 
  don't know in advance who will contact you, and even if you did, you wouldn't 
  know when that host wanted to contact you. In other words, servers are like 
  receptionists who sit by the phone and wait for incoming calls. They don't 
  know who will call or when, only that when the phone rings, they have to pick 
  it up and talk to whoever is there. We can't program that behavior with the 
  Socket class alone. Granted, there's no reason that 
  clients written in Java have to talk to Java servers--in fact, a client 
  doesn't care what language the server was written in or what platform it runs 
  on. However, if Java didn't let us write servers, there would be a glaring 
  hole in its capabilities.  Fortunately, there's no such hole. Java provides a 
  ServerSocket class to allow programmers to write servers. 
  Basically, a server socket's job is to sit by the phone and wait for incoming 
  calls. More technically, a ServerSocket runs on the 
  server and listens for incoming TCP connections. Each 
  ServerSocket listens on a particular port on the server 
  machine. When a client Socket on a remote host 
  attempts to connect to that port, the server wakes up, negotiates the 
  connection between the client and the server, and opens a regular 
  Socket between the two hosts. In other words, server 
  sockets wait for connections while client sockets initiate connections. Once 
  the server Socket has set up the connection, the server uses a regular 
  Socket object to send data to the client. Data always 
  travels over the regular socket.  The ServerSocket Class The ServerSocket class contains 
  everything you need to write servers in Java. It has constructors that create 
  new ServerSocket objects, methods that listen for 
  connections on a specified port, and methods that return a 
  Socket object when a connection is made so that you can 
  send and receive data. In addition, it has methods to set various options and 
  the usual miscellaneous methods such as toString( 
  ). The basic life cycle of a server is: 
    A new ServerSocket is 
    created on a particular port using a ServerSocket( 
    ) constructor. 
    The ServerSocket listens for 
    incoming connection attempts on that port using its 
    accept( ) method. accept( ) 
    blocks until a client attempts to make a connection, at which point 
    accept( ) returns a Socket 
    object connecting the client and the server. 
    Depending on the type of server, either the 
    Socket's getInputStream( ) 
    method, getOutputStream( ) method, or both are 
    called to get input and output streams that communicate with the client. 
    The server and the client interact according to an 
    agreed-upon protocol until it is time to close the connection. 
    The server, the client, or both close the connection. 
    The server returns to step 2 and waits for the next 
    connection. 
 If step 4 is likely to take a long or indefinite amount of time, 
  traditional Unix servers such as wu-ftpd create a new process to handle each 
  connection so that multiple clients can be serviced at the same time. Java 
  programs should spawn a thread to interact with the client so that the server 
  can be ready to process the next connection sooner. A thread places a far 
  smaller load on the server than a complete child process. In fact, the 
  overhead of forking too many processes is why the typical Unix FTP server 
  can't handle more than roughly 400 connections without slowing to a crawl. On 
  the other hand, if the protocol is simple and quick and allows the server to 
  close the connection when it's through, then it will be more efficient for the 
  server to process the client request immediately without spawning a 
thread. The operating system stores incoming connection requests 
  addressed to a particular port in a first-in, first-out queue. The default 
  length of the queue is normally 50, though this can vary from operating system 
  to operating system. Some operating systems (though not Solaris) have a 
  maximum queue length, typically five. On these systems, the queue length will 
  be the largest possible value less than or equal to 50. After the queue fills 
  to capacity with unprocessed connections, the host refuses additional 
  connections on that port until slots in the queue open up. Many (though not 
  all) clients will try to make a connection multiple times if their initial 
  attempt is refused. Managing incoming connections and the queue is a service 
  provided by the operating system; your program does not need to worry about 
  it. Several ServerSocket constructors allow you to 
  change the length of the queue if its default length isn't large enough; 
  however, you won't be able to increase the queue beyond the maximum size that 
  the operating system supports: The Constructors There are three public ServerSocket 
  constructors:  public ServerSocket(int port) throws IOException, BindException
public ServerSocket(int port, int queueLength) 
 throws IOException, BindException
public ServerSocket(int port, int queueLength, InetAddress bindAddress) 
 throws IOException
 These constructors let you specify the port, the length of the 
  queue used to hold incoming connection requests, and the local network 
  interface to bind to. They pretty much all do the same thing, though some use 
  default values for the queue length and the address to bind to. Let's explore 
  these in order.  public ServerSocket(int port) throws IOException, BindException This constructor creates a server Socket on the port specified 
  by the argument. If you pass 0 for the port number, the system selects an 
  available port for you. A port chosen for you by the system is sometimes 
  called an anonymous port since you don't know its 
  number. For servers, anonymous ports aren't very useful because clients need 
  to know in advance which port to connect to; however, there are a few 
  situations (which we will discuss later) in which an anonymous port might be 
  useful.  For example, to create a server Socket that would be used by an 
  HTTP server on port 80, you would write: 
try {
  ServerSocket httpd = new ServerSocket(80);
}
catch (IOException e) {
  System.err.println(e);
}
The constructor throws an IOException 
  (specifically, a BindException) if the Socket 
  cannot be created and bound to the requested port. An 
  IOException when creating a 
  ServerSocket almost always means one of two things. 
  Either another server socket, possibly from a completely different program, is 
  already using the requested port, or you're trying to connect to a port from 1 
  to 1023 on Unix without root (superuser) privileges. You can use this constructor to write a variation on the 
  PortScanner programs of the previous chapter. Example 11-1 checks for ports on the local machine by attempting to create 
  ServerSocket objects on them and seeing on which ports 
  that fails. If you're using Unix and are not running as root, this program 
  works only for ports 1,024 and above. Example 11-1: Look for Local Ports 
 
import java.net.*;
import java.io.*;
 
public class LocalPortScanner {
 
  public static void main(String[] args) {
    
    for (int port = 1; port <= 65535; port++) {
 
      try {
        // the next line will fail and drop into the catch block if
        // there is already a server running on the port
        ServerSocket server = new ServerSocket(port);
      }
      catch (IOException e) {
        System.out.println("There is a server on port " + port + ".");
      } // end try
 
    } // end for
 
  }
  
}
Here's the output I got when running LocalPortScanner on my NT workstation: 
D:\JAVA\JNP2\examples\11> java LocalPortScanner
There is a server on port 135.
There is a server on port 1025.
There is a server on port 1026.
There is a server on port 1027.
There is a server on port 1028.
 public ServerSocket(int port, int queueLength) throws IOException, BindException This constructor creates a ServerSocket on the specified port with a queue length of our choosing. If the machine has multiple network interfaces or IP addresses, then it listens on this port on all those interfaces and IP addresses. The queueLength argument sets the length of the queue for incoming connection requests--that is, how many incoming connections can be stored at one time before the host starts refusing connections. Some operating systems have a maximum queue length, typically five. If you try to expand the queue past that maximum number, the maximum queue length is used instead. If you pass 0 for the port number, the system selects an available port. For example, to create a server Socket on port 5,776 that would 
  hold up to 100 incoming connection requests in the queue, you would write: try {
  ServerSocket httpd = new ServerSocket(5776, 100);
}
catch (IOException e) {
  System.err.println(e);
}
The constructor throws an IOException 
  (specifically, a BindException) if the Socket 
  cannot be created and bound to the requested port. An 
  IOException when creating a 
  ServerSocket almost always means one of two things. 
  Either the specified port is already in use, or you do not have root 
  privileges on Unix and you're trying to connect to a port from 1 to 1,023. public ServerSocket(int port, int queueLength, InetAddress
  bindAddress) throws BindException, IOException
 This constructor, which is available only in Java 1.1 and later, 
  creates a ServerSocket on the specified port with 
  the specified queue length. This ServerSocket binds 
  only to the specified local IP address. This constructor is useful for servers 
  that run on systems with several IP addresses (a common practice at web server 
  farms) because it allows you to choose the address to which you'll listen. 
  That is, this ServerSocket listens only for 
  incoming connections on the specified address; it won't listen for connections 
  that come in through the host's other addresses. The other constructors bind 
  to all local IP addresses by default.  For example, metalab.unc.edu is a particular SPARCstation 
  in North Carolina. It's connected to the Internet with the IP address 
  152.2.254.81. The same SPARCstation is also called www.gigabit-ethernet.org, but with a different IP address 
  (152.2.254.82). To create a server Socket that listens on port 5,776 of 
  metalab.unc.edu but not on port 5,776 of www.gigabit-ethernet.org, you would write: try {
  ServerSocket httpd = new ServerSocket(5776, 10, 
   InetAddress.getHostByName("metalab.unc.edu"));
}
catch (IOException e) {
  System.err.println(e);
}
The constructor throws an IOException 
  (again, really a BindException) if the Socket 
  cannot be created and bound to the requested port. A 
  BindException when creating a 
  ServerSocket almost always means one of two things. 
  Either the specified port is already in use, or you do not have root 
  privileges on Unix and you're trying to connect to a port from 1 to 1,023. Accepting and Closing Connections A ServerSocket generally operates in 
  a loop that repeatedly accepts connections. Each pass through the loop invokes 
  the accept( ) method. This returns a 
  Socket object representing the connection between the 
  remote client and the local server. Interaction with the client takes place 
  through this Socket object. When the transaction is 
  finished, the server should invoke the Socket 
  object's qclose( ) method and get ready to process 
  the next incoming connection. However, when the server needs to shut down and 
  not process any further incoming connections, you should invoke the 
  ServerSocket object's qclose( ) 
  method. public Socket accept( ) throws IOException When server setup is done and you're ready to accept a 
  connection, call the ServerSocket's 
  accept( ) method. This method "blocks": it stops the flow 
  of execution and waits until a client connects. When a client does connect, 
  the accept( ) method returns a 
  Socket object. You use the streams returned by this 
  Socket's getInputStream( ) and 
  getOutputStream( ) methods to communicate with the 
  client. For example: ServerSocket server = new ServerSocket(5776);
while (true) {
  Socket connection = server.accept(  );
  OutputStreamWriter out 
   = new OutputStreamWriter(connection.getOutputStream(  ));
  out.write("You've connected to this server. Bye-bye now.\r\n");        
  connection.close(  );
}
If you don't want your program to halt while it waits for a 
  connection, put the call to accept( ) in a separate 
  thread. When you add exception handling, the code becomes somewhat more 
  convoluted. It's important to distinguish between exceptions thrown by the 
  ServerSocket, which 
  should probably shut down the server and log an error message, and exceptions 
  thrown by a Socket, 
  which should just close that active connection. Exceptions thrown by 
  the accept( ) method are an intermediate case that 
  can go either way. To do this, you'll need to nest your 
  try blocks. Finally, most servers will want to make sure 
  that all sockets they accept are closed when they're finished. Even if the 
  protocol specifies that clients are responsible for closing connections, 
  clients do not always strictly adhere to the protocol. The call to 
  qclose( ) also has to be wrapped in a 
  try block that catches an 
  IOException. However, if you do catch an 
  IOException when closing the socket, ignore it. It just 
  means that the client closed the Socket before the server could. Here's a 
  slightly more realistic example: try {
  ServerSocket server = new ServerSocket(5776);
  while (true) {
    Socket connection = server.accept(  );
    try {
      OutputStreamWriter out 
       = new OutputStreamWriter(connection.getOutputStream(  ));
      out.write("You've connected to this server. Bye-bye now.\r\n");        
      connection.close(  );
   }
   catch (IOException e) {
     // This tends to be a transitory error for this one connection;
     // e.g. the client broke the connection early. Consequently,
     // we don't want to break the loop or print an error message.
     // However, you might choose to log this exception in an error log.
   }
   finally {
     // Most servers will want to guarantee that sockets are closed
     // when complete. 
     try {
       if (connection != null) connection.close(  );
     }
     catch (IOException e) {}
   }
}
catch (IOException e) {
  System.err.println(e);
}
Example 11-2 implements a simple daytime server, as per RFC 867. Since this server 
  just sends a single line of text in response to each connection, it processes 
  each connection immediately. More complex servers should spawn a thread to 
  handle each request. In this case, the overhead of spawning a thread would be 
  greater than the time needed to process the request. 
    NOTE: If you run this program on a Unix box, you need 
    to run it as root in order to connect to port 13. If you don't want to or 
    can't run it as root, change the port number to something above 1024, say 
    1313. Example 11-2: A Daytime Server 
 import java.net.*;
import java.io.*;
import java.util.Date;
  
public class DaytimeServer {
  
  DEFAULT_PORT DEFAULT_PORT = 13;
 
  public static void main(String[] args) {
 
   int port = DEFAULT_PORT;     
   if (args.length > 0) {
     try {
        port = Integer.parseInt(args[0]);
        if (port < 0 || port >= 65536) {
          System.out.println("Port must between 0 and 65535");
          return;      
        }
     }   
     catch (NumberFormatException e) {
       // use default port
     }  
 
   }     
 
   try {
    
     ServerSocket server = new ServerSocket(port);
      
     Socket connection = null;
     while (true) {
        
       try {
         connection = server.accept(  );
         OutputStreamWriter out 
          = new OutputStreamWriter(connection.getOutputStream(  ));
         Date now = new Date(  );
         out.write(now.toString(  ) +"\r\n");
         out.flush(  );      
         connection.close(  );
       }
       catch (IOException e) {}
       finally {
         try {
           if (connection != null) connection.close(  );
         }
         catch (IOException e) {}          
       }
         
     }  // end while
       
   }  // end try
   catch (IOException e) {
     System.err.println(e);
   } // end catch
 
  } // end main
 
} // end DaytimeServer
Example 11-2 is straightforward. The first three lines import the usual packages, 
  java.io and java.net, as 
  well as java.util.Date so we can get the time. 
  There is a single DEFAULT_PORT field 
  (i.e., a constant) in the class DEFAULT_PORT, which 
  is set to the well-known port for a daytime server (port 13). The class has a 
  single method, main( ), which does all the work. If 
  the port is specified on the command-line, then it's read from 
  args[0]. Otherwise, the default port is used.  The outer try block traps any 
  IOExceptions that may arise while the 
  ServerSocket server is constructed on the daytime port or 
  when it accepts connections. The inner try block 
  watches for exceptions thrown while the connections are accepted and 
  processed. The accept( ) method is called within an 
  infinite loop to watch for new connections; like many servers, this program 
  never terminates but continues listening until an exception is thrown or you 
  stop it manually.[1] When a client makes a connection, accept( 
  ) returns a Socket, which is stored in the 
  local variable connection, and the program 
  continues. We call getOutputStream( ) to get the 
  output stream associated with that Socket and then 
  chain that output stream to a new 
  OutputStreamWriter, out. To 
  get the current Date, we construct a new Date 
  object and send it to the client by writing its string representation on 
  out with write( ). Finally, after the data is sent or an exception has been thrown, 
  we close connection inside the 
  finally block. Always close a Socket when you're finished 
  with it. In the previous chapter, we said that a client shouldn't rely on the 
  other side of a connection to close the socket. That goes triple for servers. 
  Clients can time out or crash; users can cancel transactions; networks can go 
  down in high-traffic periods. For any of these or a dozen more reasons, you 
  cannot rely on clients to close sockets, even when the protocol requires them 
  to (which it doesn't in this case). Sending binary, nontext data is not significantly harder. Example 11-3 demonstrates with a time server. This follows the time protocol 
  outlined in RFC 868. When a client connects, the server sends a 4-byte, 
  big-endian, unsigned integer specifying the number of seconds that have passed 
  since 12:00 A.M., January 1, 1900 GMT (the epoch). The current time can be 
  retrieved simply by creating a new Date object. 
  However, since the Date class counts milliseconds 
  since 12:00 A.M., January 1, 1970 GMT rather than seconds since 12:00 A.M., 
  January 1, 1900 GMT, some conversion is necessary.  Example 11-3: A Time Server  import java.net.*;
import java.io.*;
import java.util.Date;
  
public class TimeServer {
  
  DEFAULT_PORT DEFAULT_PORT = 37;
 
  public static void main(String[] args) {
        
   int port = DEFAULT_PORT;     
   if (args.length > 0) {
     try {
        port = Integer.parseInt(args[0]);
        if (port < 0 || port >= 65536) {
          System.out.println("Port must between 0 and 65535");
          return;      
        }
     }   
     catch (NumberFormatException e) {}  
   }     
 
   // The time protocol sets the epoch at 1900,
   // the java Date class at 1970. This number 
   // converts between them.
    
   long differenceBetweenEpochs = 2208988800L;
    
   try {
     ServerSocket server = new ServerSocket(port);
       while (true) {
         Socket connection = null;
         try {
           connection = server.accept(  );
           OutputStream out = connection.getOutputStream(  );
           Date now = new Date(  );
           long msSince1970 = now.getTime(  );
           long secondsSince1970 = msSince1970/1000;
           long secondsSince1900 = secondsSince1970 
            + differenceBetweenEpochs;
           byte[] time = new byte[4];
           time[0] 
            = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24);
           time[1] 
            = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
           time[2] 
            = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
           time[3] = (byte) (secondsSince1900 & 0x00000000000000FFL);
           out.write(time);
           out.flush(  );      
         } // end try
         catch (IOException e) {
         } // end catch
         finally {
           if (connection != null) connection.close(  ); 
         }
       }  // end while
   }  // end try
   catch (IOException e) {
     System.err.println(e);
   } // end catch
 
  } // end main
 
} // end TimeServer
As with the TimeClient of the 
  previous chapter, most of the effort here goes into working with a data format 
  (32-bit unsigned integers) that Java doesn't natively support.  public void qclose( ) throws IOException If you're finished with a server socket, you should close it, 
  especially if your program is going to continue to run for some time. This 
  frees up the port for other programs that may wish to use it. Closing a 
  ServerSocket should not be confused with closing a 
  Socket. Closing a ServerSocket 
  frees a port on the local host, allowing another server to bind to the port; 
  closing a Socket breaks the connection between the 
  local and the remote hosts. Server sockets are closed automatically when a program dies, so 
  it's not absolutely necessary to close them in programs that terminate shortly 
  after the ServerSocket is no longer needed. 
  Nonetheless, it doesn't hurt. For example, the main loop of the 
  LocalPortScanner program might be better written like 
  this so that it doesn't temporarily occupy most of the ports on the 
system: 
for (int port = 1; port <= 65535; port++) {
 
  try {
    // the next line will fail and drop into the catch block if
    // there is already a server running on the port
    ServerSocket server = new ServerSocket(port);
    server.close(  );
  }
  catch (IOException e) {
    System.out.println("There is a server on port " + port + ".");
  } // end try
 
} // end for
The get Methods The ServerSocket class provides two 
  getter methods to tell you the local address and port occupied by the server 
  socket. These are useful if you've opened a server Socket on an anonymous port 
  and/or an unspecified network interface. This would be the case, for one 
  example, in the data connection of an FTP session.  public InetAddress getInetAddress( ) This method returns the address being used by the server (the 
  local host). If the local host has a single IP address (as most do), then this 
  is the address returned by InetAddress.getLocalHost( 
  ). If the local host has more than one IP address, then the specific 
  address returned is one of the host's IP addresses. You can't predict which 
  address you will get. For example: try {
  ServerSocket httpd = new ServerSocket(80);
  InetAddress ia = httpd.getInetAddress(  );
}
catch (IOException e) {
}
public int getLocalPort( ) The ServerSocket constructors allow 
  you to listen on an unspecified port by passing 0 for the port number. This 
  method lets you find out what port you're listening on. You might use this in 
  a peer-to-peer multisocket program where you already have a means to inform 
  other peers of your location. Or a server might spawn several smaller servers 
  to perform particular operations. The well-known server could inform clients 
  what ports they can find the smaller servers on. Of course, you can also use 
  getLocalPort( ) to find a non-anonymous port, but 
  why would you need to? Example 11-4 demonstrates. Example 11-4: A Random Port 
   
import java.net.*;
import java.io.*;
 
public class RandomPort {
 
  public static void main(String[] args) {
 
    try {
      ServerSocket server = new ServerSocket(0);
      System.out.println("This server runs on port " 
       + server.getLocalPort(  ));
    }
    catch (IOException e) {
      System.err.println(e);
    }
 
  }
 
}
Here's the output of several runs: D:\JAVA\JNP2\examples\11> java RandomPort
This server runs on port 1154
D:\JAVA\JNP2\examples\11> java RandomPort
This server runs on port 1155
D:\JAVA\JNP2\examples\11> java RandomPort
This server runs on port 1156
 At least on this VM, the ports aren't really random; but they 
  are at least indeterminate until runtime.  Socket Options The only Socket option supported for server sockets is 
  SO_TIMEOUT. SO_TIMEOUT is the amount of time, in milliseconds, that 
  accept( ) waits for an incoming connection before 
  throwing a java.io.InterruptedIOException. If 
  SO_TIMEOUT is 0, then accept( ) will never time 
  out. The default is to never time out. Using SO_TIMEOUT is rather rare. You might need it if you were 
  implementing a complicated and secure protocol that required multiple 
  connections between the client and the server where some responses needed to 
  occur within a fixed amount of time. Most servers are designed to run for 
  indefinite periods of time and therefore use the default timeout value, which 
  is 0 (never time out). public void setSoTimeout(int timeout) throws SocketException The setSoTimeout( ) method sets the 
  SO_TIMEOUT field for this server Socket object. The countdown starts when 
  accept( ) is invoked. When the timeout expires, 
  accept( ) throws an 
  InterruptedIOException. You should set this option before 
  calling accept( ); you cannot change the timeout 
  value while accept( ) is waiting for a connection. 
  The timeout argument must be greater than or equal 
  to zero; if it isn't, the method throws an 
  IllegalArgumentException. For example: 
try {
  ServerSocket server = new ServerSocket(2048);
  server.setSoTimeout(30000); // block for no more than 30 seconds
  try {
    Socket s = server.accept(  );
    // handle the connection
    // ...
  }
  catch (InterruptedIOException e) {
    System.err.println("No connection within 30 seconds");
  }
  finally {
    server.close(  );
  }
catch (IOException e) {
  System.err.println("Unexpected IOException: " + e);
}
public int getSoTimeout( ) throws IOException The getSoTimeout( ) method returns this server socket's current SO_TIMEOUT value. For example: public void printSoTimeout(ServerSocket server) {
 
  int timeout = server.getSoTimeOut(  );
  if (timeout > 0) {
    System.out.println(server + " will time out after " 
     + timeout + "milliseconds.");
  }
  else if (timeout == 0) {
    System.out.println(server + " will never time out.");
  }
  else {
    System.out.println("Impossible condition occurred in " + server);
    System.out.println("Timeout cannot be less than zero." );
  }
 
}
The Object Methods jServerSocket overrides only one of 
  the standard methods from java.lang.Object, 
  toString( ). Thus, equality comparisons test for strict 
  identity, and server sockets are problematic in hash tables. Normally, this 
  isn't a large problem.  public String toString( ) A String returned by 
  ServerSocket's toString( ) 
  method looks like this: ServerSocket[addr=0.0.0.0,port=0,localport=5776] In current implementations, addr is 
  always 0.0.0.0 and port is always 0. Presumably, 
  these may become something more interesting in the future. The 
  localport is the local port on which the server is 
  listening for connections. Implementation The ServerSocket class provides two 
  methods for changing the default implementation of server sockets. I'll 
  describe them only briefly here, since they're primarily intended for 
  implementers of Java virtual machines rather than application programmers. public static synchronized void setSocketFactory (SocketImpl 
  Factory fac) throws IOException This method sets the system's server 
  SocketImplFactory, which is the factory used to 
  create ServerSocket objects. This is not the same 
  factory that is used to create client Socket 
  objects, though the syntax is similar; you can have one factory for 
  Socket objects and a different factory for 
  ServerSocket objects. You can set this factory only once 
  in a program, however. A second attempt to set the 
  SocketImplFactory throws a 
  SocketException.  Protected final void implAccept(Socket s) throws 
  IOException Subclasses of ServerSocket use this 
  method to implement accept( ). You pass an 
  unconnected Socket object to 
  implaccept( ). (Doing this requires you to subclass 
  Socket as well since the standard 
  java.net.Socket class doesn't provide a means to create 
  unconnected sockets.) When the method returns, the 
  Socket argument s is connected 
  to a client. Some Useful Servers This section shows several servers you can build with server 
  sockets. It starts with a server you can use to test client responses and 
  requests, much as you use Telnet to test server behavior. Then we present 
  three different HTTP servers, each with a different special purpose and each 
  slightly more complex than the previous one.  Client Tester In the previous chapter, you learned how to use Telnet to 
  experiment with servers. There's no equivalent program to test clients, so 
  let's create one. Example 11-5 is a program called ClientTester that runs 
  on a port specified on the command-line, shows all data sent by the client, and 
  allows you to send a response to the client by typing it on the command line. 
  For example, you can use this program to see the commands that Netscape 
  Navigator sends to a server. 
    NOTE: Clients are rarely as forgiving about unexpected 
    server responses as servers are about unexpected client responses. If at all 
    possible, try to run the clients that connect to this program on a Unix 
    system or some other platform that is moderately crash-proof. Don't run them 
    on a Mac or Windows 98, which are less stable. This program uses two threads: one to handle input from the 
  client and the other to send output from the server. Using two threads allows 
  the program to handle input and output simultaneously: it can be sending a 
  response to the client while receiving a request--or, more to the point, it 
  can send data to the client while waiting for the client to respond. This is 
  convenient because different clients and servers talk in unpredictable ways. 
  With some protocols, the server talks first; with others, the client talks 
  first. Sometimes the server sends a one-line response; often, the response is 
  much larger. Sometimes the client and the server talk at each other 
  simultaneously. Other times, one side of the connection waits for the other to 
  finish before it responds. The program must be flexible enough to handle all 
  these cases. Example 11-5 shows the code. Example 11-5: A Client Tester 
 
import java.net.*;
import java.io.*;
import com.macfaq.io.SafeBufferedReader; // from Chapter 4
 
 
public class ClientTester {
 
  public static void main(String[] args) {
 
    int port;
    
    try {
      port = Integer.parseInt(args[0]);
    }  
    catch (Exception e) {
      port = 0;
    }
    
    try {
      ServerSocket server = new ServerSocket(port, 1);
      System.out.println("Listening for connections on port " 
       + server.getLocalPort(  ));
 
      while (true) {
        Socket connection = server.accept(  );
        try {
          System.out.println("Connection established with " 
           + connection);
          Thread input = new InputThread(connection.getInputStream(  ));
          input.start(  );
          Thread output 
           = new OutputThread(connection.getOutputStream(  ));
          output.start(  );
          // wait for output and input to finish 
          try {
            input.join(  );
            output.join(  );
          }
          catch (InterruptedException e) {
          }
        }
        catch (IOException e) {
          System.err.println(e); 
        }
        finally {
          try {
            if (connection != null) connection.close(  );
          }
          catch (IOException e) {}
        }
      }
    }
    catch (IOException e) {
      e.printStackTrace(  );
    }
  
  }
 
}
 
class InputThread extends Thread {
  
  InputStream in;
  
   public InputThread(InputStream in) {
     this.in = in;
   }
 
   public void run(  )  {
   
     try {     
       while (true) {
         int i = in.read(  );
         if (i == -1) break;
         System.out.write(i);
       }
     }
     catch (SocketException e) {
       // output thread closed the socket
     }
     catch (IOException e) {
       System.err.println(e);
     }
     try {
       in.close(  );
     }
     catch (IOException e) { 
     } 
 
  }
 
}
 
class OutputThread extends Thread {
  
  Writer out;
    
  public OutputThread(OutputStream out) {
    this.out = new OutputStreamWriter(out);
  }
 
  public void run(  ) {
 
    String line;
    BufferedReader in 
     = new SafeBufferedReader(new InputStreamReader(System.in));
    try {
      while (true) {
        line = in.readLine(  );
        if (line.equals(".")) break;
        out.write(line +"\r\n");
        out.flush(  );
      }   
    }
    catch (IOException e) { 
    } 
    try {
      out.close(  );
    }
    catch (IOException e) { 
    } 
    
   }
 
}
The client tester application is split into three classes: 
  ClientTester, InputThread, and 
  OutputThread. The 
  ClientTester class reads the port from the command-line, 
  opens a ServerSocket on that port, and listens for 
  incoming connections. Only one connection is allowed at a time, because this 
  program is designed for experimentation, and a slow human being has to provide 
  all responses. Consequently, we set an unusually short queue length of 1. 
  Further connections will be refused until the first one has been closed.  An infinite while loop waits for 
  connections with the accept( ) method. When a 
  connection is detected, its InputStream is used to 
  construct a new InputThread, and its 
  OutputStream is used to construct a new 
  OutputThread. After starting these threads, we wait for 
  them to finish by calling their join( ) 
methods. The InputThread is contained almost 
  entirely in the run( ) method. It has a single 
  field, in, which is the 
  InputStream from which data will be read. Data is read 
  from in one byte at a time. Each 
  byte read is written on 
  System.out. The run( ) method 
  ends when the end of stream is encountered or an 
  IOException is thrown. The most likely exception here is 
  a SocketException thrown because the corresponding 
  OutputThread closed the connection. The OutputThread reads input from the 
  local user sitting at the terminal and sends that data to the client. Its 
  constructor has a single argument, an output stream for sending data to the 
  client. OutputThread reads input from the user on 
  System.in, which is chained to an instance of the 
  SafeBufferedReader class developed in Chapter 4, Java I/O. The 
  OutputStream that was passed to the constructor is 
  chained to an OutputStreamWriter for convenience. 
  The run( ) method for 
  OutputThread reads lines from the 
  SafeBufferedReader, and copies them onto the 
  OutputStreamWriter, which sends them to the client. A 
  period typed on a line by itself signals the end of user input. When this 
  occurs, run( ) exits the loop and 
  out is closed. This has the effect of also closing the 
  socket, so that a SocketException is thrown in the 
  input thread, which also exits.  For example, here's the output when Netscape Communicator 4.6 
  for Windows connected to this server: 
D:\JAVA\JNP2\examples\11> java ClientTester 80
Listening for connections on port 80
Connection established with 
Socket[addr=localhost/127.0.0.1,port=1033,localport=80]
GET / HTTP 1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 [en] (WinNT; I)
Host: localhost
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
 
 <html><body><h1>Hello Client!</h2></body></html>
 .
 |