Each connection has two ends: the client, which initiates the connection, and the server, which responds to the connection. So far, I've only discussed the client side and assumed that a server existed out there for the client to talk to. To implement a server, you need to write a program that waits for other hosts to connect to it. A server socket binds to a particular port on the local machine (the server). Then it listens for incoming connection attempts from remote machines (the clients). When the server detects a connection attempt, it accepts the connection. This creates a socket between the two machines over which the client and the server communicate.
Multiple clients can connect to a server simultaneously. Incoming data is distinguished by the port to which it is addressed and the client host and port from which it came. The server can tell which service (such as HTTP or FTP) the data is intended for by checking the port at which it arrives. It knows where to send any response by looking at the client address and port stored with the data.
No more than one server socket can listen to a particular port at one time. Therefore, since a server may need to handle many connections at once, server programs tend to be multithreaded. (Alternately, they can use nonblocking I/O. We'll explore this starting in Chapter 16.) Generally, the server socket listening on the port only accepts the connections. It passes off the actual processing of each connection to a separate thread. Incoming connections are stored in a queue until the server can accept them. On most systems, the default queue length is between 5 and 50. Once the queue fills up, further incoming connections are refused until space in the queue opens up.
The java.net.ServerSocket class represents a server socket. The constructors receive the port to bind to, the queue length for incoming connections, and the IP address:
public ServerSocket(int port) throws IOException public ServerSocket(int port, int backlog) throws IOException public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException
Normally, you only specify the port you want to listen on:
ServerSocket ss = new ServerSocket(80);
When you create a ServerSocket object, it attempts to bind to the port given by the port argument. If another server socket is already listening to the port, the constructor throws an IOExceptionmore specifically, a java.net.BindException. Only one server socket can listen to a particular port at a time. This includes server sockets opened by non-Java programs. For example, if there's already an HTTP server running on port 80, you won't be able to bind to port 80.
|
0 is a special port number. It tells Java to pick an available port. You can then find out which port it has picked with the getLocalPort( ) method:
public int getLocalPort( )
This is useful if the client and the server have already established a separate channel of communication over which the chosen port number can be communicated. For example, the FTP protocol uses two sockets. The client makes the initial connection to the server on a socket it will use to send commands. The client also opens a server socket on a random port on the local host. One of the commands it sends tells the server the port number on which the client is listening. The server then opens a socket to the client's server port, which it uses to send files. Because commands and data are sent over two different sockets, a long file doesn't tie up the command channel.
Once you have a ServerSocket, you wait for incoming connections by calling the accept( ) method. This method blocks until a connection attempt occurs and then returns a Socket that you can use to communicate with the client.
public Socket accept( ) throws IOException
The close( ) method terminates the ServerSocket:
public void close( ) throws IOException
That's pretty much all there is to the ServerSocket, except for a few methods dealing with socket options and some other details. In particular, there aren't methods for getting input and output streams. Instead, accept( ) returns a client Socket object: this Socket's getInputStream( ) or getOutputStream( ) methods return the streams used to communicate. For example:
ServerSocket ss = new ServerSocket(2345); Socket s = ss.accept( ); OutputStream out = s.getOutputStream( ); // send data to the client... s.close( );
Notice in this example, I closed the Socket s, not the ServerSocket ss. ss is still bound to port 2345. You get a new socket for each connection and reuse the server socket. For example, the next code fragment repeatedly accepts connections:
ServerSocket ss = new ServerSocket(2345); while (true) { Socket s = ss.accept( ); OutputStream out = s.getOutputStream( ); // send data to the client... s.close( ); }
The program in Example 5-4 listens for incoming connections on port 2345. When it detects one, it answers with the client's address and port and its own. Then it closes the connection.
Example 5-4. The HelloServer program
import java.net.*; import java.io.*; public class HelloServer { public static void main(String[] args) throws IOException { int port = 2345; ServerSocket ss = new ServerSocket(port); while (true) { try { Socket s = ss.accept( ); String response = "Hello " + s.getInetAddress( ) + " on port " + s.getPort( ) + " "; response += "This is " + s.getLocalAddress( ) + " on port " + s.getLocalPort( ) + " "; OutputStream out = s.getOutputStream( ); out.write(response.getBytes("US-ASCII")); out.flush( ); s.close( ); } catch (IOException ex) { // This is an error on one connection. Maybe the client crashed. // Maybe it broke the connection prematurely. Whatever happened, // it's not worth shutting down the server for. } } // end while } // end main } // end HelloServer |
Here's some output from this server. The server is running on utopia.poly.edu. The client is connecting from titan.oit.unc.edu. Note how the port from which the connection comes changes each time; like most client programs, the telnet program picks an arbitrary local port for outgoing connections:
$ telnet utopia.poly.edu Trying 128.238.3.21... Connected to utopia.poly.edu. Escape character is '^]'. Hello titan.oit.unc.edu/152.2.22.14 on port 50361 This is utopia.poly.edu/128.238.3.21 on port 2345 Connection closed by foreign host. % telnet utopia.poly.edu Trying 128.238.3.21... Connected to utopia.poly.edu. Escape character is '^]'. Hello titan.oit.unc.edu/152.2.22.14 on port 50362 This is utopia.poly.edu/128.238.3.21 on port 2345 Connection closed by foreign host.
|
Basic I/O
Introducing I/O
Output Streams
Input Streams
Data Sources
File Streams
Network Streams
Filter Streams
Filter Streams
Print Streams
Data Streams
Streams in Memory
Compressing Streams
JAR Archives
Cryptographic Streams
Object Serialization
New I/O
Buffers
Channels
Nonblocking I/O
The File System
Working with Files
File Dialogs and Choosers
Text
Character Sets and Unicode
Readers and Writers
Formatted I/O with java.text
Devices
The Java Communications API
USB
The J2ME Generic Connection Framework
Bluetooth
Character Sets