6.6 ServerSocket Class
A special type of socket, the server socket, is used to provide TCP services. Client sockets bind to any free port on the local machine, and connect to a specific server port and host. The difference with server sockets is that they bind to a specific port on the local machine, so that remote clients may locate a service. Client socket connections will connect to only one machine, whereas server sockets are capable of fulfilling the requests of multiple clients.
The way it works is simpleclients are aware of a service running on a particular port (usually the port number is well known, and used for particular protocols, but servers may run on nonstandard port numbers as well). They establish a connection, and within the server, the connection is accepted. Multiple connections can be accepted at the same time, or a server may choose to accept only one connection at any given moment. Once accepted, the connection is represented as a normal socket, in the form of a Socket objectonce you have mastered the Socket class, it becomes almost as simple to write servers as it does clients. The only difference between a server and a client is that the server binds to a specific port, using a ServerSocket object. This ServerSocket object acts as a factory for client connectionsyou don't need to create instances of the Socket class yourself. These connections are modeled as a normal socket, so you can connect input and output filter streams (or even a reader and writer) to the connection.
6.6.1 Creating a ServerSocket
Once a server socket is created, it will be bound to a local port and ready to accept incoming connections. When clients attempt to connect, they are placed into a queue. Once all free space in the queue is exhausted, further clients will be refused.
Constructors
The simplest way to create a server socket is to bind to a local address, which is specified as the only parameter, using a constructor. For example, to provide a service on port 80 (usually used for Web servers), the following snippet of code would be used:
try { // Bind to port 80, to provide a TCP service (like HTTP) ServerSocket myServer = new ServerSocket ( 80 ); // ...... } catch (IOException ioe) { System.err.println ("I/O error " + ioe); }
This is the simplest form of the ServerSocket constructor, but there are several others that allow additional customization. All of these constructors are marked as public.
ServerSocket(int port) throws java.io.IOException, java.lang.SecurityExceptionbinds the server socket to the specified port number, so that remote clients may locate the TCP service. If a value of zero is passed, any free port will be usedhowever, clients will be unable to access the service unless notified somehow of the port number. By default, the queue size is set to 50, but an alternate constructor is provided that allows modification of this setting. If the port is already bound, or security restrictions (such as security polices or operating system restrictions on well-known ports) prevent access, an exception is thrown.
ServerSocket(int port, int numberOfClients) throws java.io.IOException, java.lang.SecurityExceptionbinds the server socket to the specified port number and allocates sufficient space to the queue to support the specified number of client sockets. This is an overloaded version of the ServerSocket(int port) constructor, and if the port is already bound or security restrictions prevent access, an exception is thrown.
ServerSocket(int port, int numberOfClients, InetAddress address) throws java.io.IOException, java.lang.SecurityExceptionbinds the server socket to the specified port number, and allocates sufficient space to the queue to support the specified number of client sockets. This is an overloaded version of the ServerSocket(int port, int numberOfClients) constructor that allows a server socket to bind to a specific IP address, in the case of a multihomed machine. For example, a machine may have two network cards, or may be configured to represent itself as several machines by using virtual IP addresses. Specifying a null value for the address will cause the server socket to accept requests on all local addresses. If the port is already bound or security restrictions prevent access, an exception is thrown.
6.6.2 Using a ServerSocket
While the Socket class is fairly versatile, and has many methods, the Server Socket class doesn't really do that much, other than accept connections and act as a factory for Socket objects that model the connection between client and server. The most important method is the accept() method, which accepts client connection requests, but there are several others that developers may find useful.
Methods
All methods are public unless otherwise noted.
Socket accept() throws java.io.IOException, java.lang.Security Exceptionwaits for a client to request a connection to the server socket, and accepts it. This is a blocking I/O operation, and will not return until a connection is made (unless the timeout socket option is set). When a connection is established, it will be returned as a Socket object. When accepting connections, each client request will be verified by the default security manager, which makes it possible to accept certain IP addresses and block others, causing an exception to be thrown. However, servers do not need to rely on the security manager to block or terminate connectionsthe identity of a client can be determined by calling the getInetAddress() method of the client socket.
void close() throws java.io.IOExceptioncloses the server socket, which unbinds the TCP port and allows other services to use it.
InetAddress getInetAddress()returns the address of the server socket, which may be different from the local address in the case of a multihomed machine (i.e., a machine whose localhost is known by two or more IP addresses).
int getLocalPort()returns the port number to which the server socket is bound.
int getSoTimeout() throws java.io.IOExceptionreturns the value of the timeout socket option, which determines how many milliseconds an accept() operation can block for. If a value of zero is returned, the accept operation blocks indefinitely.
void implAccept(Socket socket) throws java.io.IOExceptionthis method allows ServerSocket subclasses to pass an unconnected socket subclass, and to have that socket object accept an incoming request. Using the implAccept method to accept the connection, an overridden ServerSocket.accept() method can return a connected socket. Few developers will want to subclass the ServerSocket, and using this should be avoided unless required.
static void setSocketFactory ( SocketImplFactory factory ) throws java.io.IOException, java.net.SocketException, java.lang.SecurityException assigns a server socket factory for the JVM. This is a static method, and should be called only once during the lifetime of a JVM. If assigning a new socket factory is prohibited, or one has already been assigned, an exception is thrown.
void setSoTimeout(int timeout) throws java.net.SocketExceptionassigns a timeout value (specified in milliseconds) for the blocking accept() operation. If a value of zero is specified, timeouts are disabled and the operation will block indefinitely. Providing timeouts are enabled, however, whenever the accept() method is called a timer starts. When the timer expires, a java.io.InterruptedIOException is thrown, which allows a server to then take further actions.
6.6.3 Accepting and Processing Requests from TCP Clients
The most important function of a server socket is to accept client sockets. Once a client socket is obtained, the server can perform all the "real work" of server programming, which involves reading from and writing to the socket to implement a network protocol. The exact data that is sent or received is dependent on the details of the protocol. For example, a mail server that provides access to stored messages would listen to commands and send back message contents. A telnet server would listen for keystrokes and pass these to a log-in shell, and send back output to the network client. Protocol-specific actions are less network- and more programming-oriented.
The following snippet shows how client sockets are accepted, and how I/O streams may be connected to the client:
// Perform a blocking read operation, to read the next socket // connection Socket nextSocket = someServerSocket.accept(); // Connect a filter reader and writer to the stream BufferedReader reader = new BufferedReader (new InputStreamReader (nextSocket.getInputStream() ) ); PrintWriter writer = new PrintWriter( new OutputStreamWriter (nextSocket.getOutputStream() ) );
From then on, the server may conduct the tasks needed to process and respond to client requests, or may choose to leave this task for code executing in another thread. Remember that just like any other form of I/O operation in Java, code will block indefinitely while reading a response from a clientso to service multiple clients concurrently, threads must be used. In simple cases, however, multiple threads of execution may not be necessary, particularly if requests are responded to quickly and take little time to process.
Creating fully-fledged client/server applications that implement popular Internet protocols involves a fair amount of effort, especially for those new to network programming. It also draws on other skills, such as multi-threaded programming, discussed in the next chapter. For now, we'll focus on a simple, bare-bones TCP server that executes as a single-threaded application.