ServerSocket.java revision a3b57e9cb41fb00ac607cd330fa73270b564b66c
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.io.IOException;
21import java.nio.channels.ServerSocketChannel;
22
23/**
24 * This class represents a server-side socket that waits for incoming client
25 * connections. A {@code ServerSocket} handles the requests and sends back an
26 * appropriate reply. The actual tasks that a server socket must accomplish are
27 * implemented by an internal {@code SocketImpl} instance.
28 */
29public class ServerSocket {
30    /**
31     * The RI specifies that where the caller doesn't give an explicit backlog,
32     * the default is 50. The OS disagrees, so we need to explicitly call listen(2).
33     */
34    private static final int DEFAULT_BACKLOG = 50;
35
36    private final SocketImpl impl;
37
38    /**
39     * @hide internal use only
40     */
41    public SocketImpl getImpl$() {
42        return impl;
43    }
44
45    static SocketImplFactory factory;
46
47    private boolean isBound;
48
49    private boolean isClosed;
50
51    /**
52     * Constructs a new unbound {@code ServerSocket}.
53     *
54     * @throws IOException if an error occurs while creating the socket.
55     */
56    public ServerSocket() throws IOException {
57        this.impl = factory != null ? factory.createSocketImpl()
58                : new PlainServerSocketImpl();
59        impl.create(true);
60    }
61
62    /**
63     * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
64     * The backlog is set to 50. If {@code port == 0}, a port will be assigned by the OS.
65     *
66     * @throws IOException if an error occurs while creating the socket.
67     */
68    public ServerSocket(int port) throws IOException {
69        this(port, DEFAULT_BACKLOG, Inet4Address.ANY);
70    }
71
72    /**
73     * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
74     * The backlog is set to {@code backlog}.
75     * If {@code port == 0}, a port will be assigned by the OS.
76     *
77     * @throws IOException if an error occurs while creating the socket.
78     */
79    public ServerSocket(int port, int backlog) throws IOException {
80        this(port, backlog, Inet4Address.ANY);
81    }
82
83    /**
84     * Constructs a new {@code ServerSocket} instance bound to the given {@code localAddress}
85     * and {@code port}. The backlog is set to {@code backlog}.
86     * If {@code localAddress == null}, the ANY address is used.
87     * If {@code port == 0}, a port will be assigned by the OS.
88     *
89     * @throws IOException if an error occurs while creating the socket.
90     */
91    public ServerSocket(int port, int backlog, InetAddress localAddress) throws IOException {
92        checkListen(port);
93        this.impl = factory != null ? factory.createSocketImpl()
94                : new PlainServerSocketImpl();
95        InetAddress addr = (localAddress == null) ? Inet4Address.ANY : localAddress;
96
97        synchronized (this) {
98            impl.create(true);
99            try {
100                impl.bind(addr, port);
101                isBound = true;
102                impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
103            } catch (IOException e) {
104                close();
105                throw e;
106            }
107        }
108    }
109
110    /**
111     * Waits for an incoming request and blocks until the connection is opened.
112     * This method returns a socket object representing the just opened
113     * connection.
114     *
115     * @return the connection representing socket.
116     * @throws IOException
117     *             if an error occurs while accepting a new connection.
118     */
119    public Socket accept() throws IOException {
120        checkOpen();
121        if (!isBound()) {
122            throw new SocketException("Socket is not bound");
123        }
124
125        Socket aSocket = new Socket();
126        try {
127            implAccept(aSocket);
128        } catch (IOException e) {
129            aSocket.close();
130            throw e;
131        }
132        return aSocket;
133    }
134
135    private void checkListen(int aPort) {
136        if (aPort < 0 || aPort > 65535) {
137            throw new IllegalArgumentException("Port out of range: " + aPort);
138        }
139    }
140
141    /**
142     * Closes this server socket and its implementation. Any attempt to connect
143     * to this socket thereafter will fail.
144     *
145     * @throws IOException
146     *             if an error occurs while closing this socket.
147     */
148    public void close() throws IOException {
149        isClosed = true;
150        impl.close();
151    }
152
153    /**
154     * Gets the local IP address of this server socket or {@code null} if the
155     * socket is unbound. This is useful for multihomed hosts.
156     *
157     * @return the local address of this server socket.
158     */
159    public InetAddress getInetAddress() {
160        if (!isBound()) {
161            return null;
162        }
163        return impl.getInetAddress();
164    }
165
166    /**
167     * Gets the local port of this server socket or {@code -1} if the socket is
168     * unbound.
169     *
170     * @return the local port this server is listening on.
171     */
172    public int getLocalPort() {
173        if (!isBound()) {
174            return -1;
175        }
176        return impl.getLocalPort();
177    }
178
179    /**
180     * Gets the socket {@link SocketOptions#SO_TIMEOUT accept timeout}.
181     *
182     * @throws IOException
183     *             if the option cannot be retrieved.
184     */
185    public synchronized int getSoTimeout() throws IOException {
186        checkOpen();
187        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
188    }
189
190    /**
191     * Invokes the server socket implementation to accept a connection on the
192     * given socket {@code aSocket}.
193     *
194     * @param aSocket
195     *            the concrete {@code SocketImpl} to accept the connection
196     *            request on.
197     * @throws IOException
198     *             if the connection cannot be accepted.
199     */
200    protected final void implAccept(Socket aSocket) throws IOException {
201        synchronized (this) {
202            impl.accept(aSocket.impl);
203            aSocket.accepted();
204        }
205    }
206
207    /**
208     * Sets the server socket implementation factory of this instance. This
209     * method may only be invoked with sufficient security privilege and only
210     * once during the application lifetime.
211     *
212     * @param aFactory
213     *            the streaming socket factory to be used for further socket
214     *            instantiations.
215     * @throws IOException
216     *             if the factory could not be set or is already set.
217     */
218    public static synchronized void setSocketFactory(SocketImplFactory aFactory) throws IOException {
219        if (factory != null) {
220            throw new SocketException("Factory already set");
221        }
222        factory = aFactory;
223    }
224
225    /**
226     * Sets the {@link SocketOptions#SO_TIMEOUT accept timeout} in milliseconds for this socket.
227     * This accept timeout defines the period the socket will block waiting to
228     * accept a connection before throwing an {@code InterruptedIOException}. The value
229     * {@code 0} (default) is used to set an infinite timeout. To have effect
230     * this option must be set before the blocking method was called.
231     *
232     * @param timeout the timeout in milliseconds or 0 for no timeout.
233     * @throws SocketException
234     *             if an error occurs while setting the option.
235     */
236    public synchronized void setSoTimeout(int timeout) throws SocketException {
237        checkOpen();
238        if (timeout < 0) {
239            throw new IllegalArgumentException("timeout < 0");
240        }
241        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
242    }
243
244    /**
245     * Returns a textual representation of this server socket including the
246     * address, port and the state. The port field is set to {@code 0} if there
247     * is no connection to the server socket.
248     *
249     * @return the textual socket representation.
250     */
251    @Override
252    public String toString() {
253        StringBuilder result = new StringBuilder(64);
254        result.append("ServerSocket[");
255        if (!isBound()) {
256            return result.append("unbound]").toString();
257        }
258        return result.append("addr=")
259                .append(getInetAddress().getHostName()).append("/")
260                .append(getInetAddress().getHostAddress()).append(
261                        ",port=0,localport=")
262                .append(getLocalPort()).append("]")
263                .toString();
264    }
265
266    /**
267     * Binds this server socket to the given local socket address with a maximum
268     * backlog of 50 unaccepted connections. If the {@code localAddr} is set to
269     * {@code null} the socket will be bound to an available local address on
270     * any free port of the system.
271     *
272     * @param localAddr
273     *            the local address and port to bind on.
274     * @throws IllegalArgumentException
275     *             if the {@code SocketAddress} is not supported.
276     * @throws IOException
277     *             if the socket is already bound or a problem occurs during
278     *             binding.
279     */
280    public void bind(SocketAddress localAddr) throws IOException {
281        bind(localAddr, DEFAULT_BACKLOG);
282    }
283
284    /**
285     * Binds this server socket to the given local socket address. If the
286     * {@code localAddr} is set to {@code null} the socket will be bound to an
287     * available local address on any free port of the system.
288     *
289     * @param localAddr the local machine address and port to bind on.
290     * @param backlog the maximum number of unaccepted connections. Passing 0 or
291     *     a negative value yields the default backlog of 50.
292     * @throws IllegalArgumentException if the {@code SocketAddress} is not
293     *     supported.
294     * @throws IOException if the socket is already bound or a problem occurs
295     *     during binding.
296     */
297    public void bind(SocketAddress localAddr, int backlog) throws IOException {
298        checkOpen();
299        if (isBound()) {
300            throw new BindException("Socket is already bound");
301        }
302        int port = 0;
303        InetAddress addr = Inet4Address.ANY;
304        if (localAddr != null) {
305            if (!(localAddr instanceof InetSocketAddress)) {
306                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
307                        localAddr.getClass());
308            }
309            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
310            if ((addr = inetAddr.getAddress()) == null) {
311                throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
312            }
313            port = inetAddr.getPort();
314        }
315
316        synchronized (this) {
317            try {
318                impl.bind(addr, port);
319                isBound = true;
320                impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
321            } catch (IOException e) {
322                close();
323                throw e;
324            }
325        }
326    }
327
328    /**
329     * Gets the local socket address of this server socket or {@code null} if
330     * the socket is unbound. This is useful on multihomed hosts.
331     *
332     * @return the local socket address and port this socket is bound to.
333     */
334    public SocketAddress getLocalSocketAddress() {
335        if (!isBound()) {
336            return null;
337        }
338        return new InetSocketAddress(getInetAddress(), getLocalPort());
339    }
340
341    /**
342     * Returns whether this server socket is bound to a local address and port
343     * or not.
344     *
345     * @return {@code true} if this socket is bound, {@code false} otherwise.
346     */
347    public boolean isBound() {
348        return isBound;
349    }
350
351    /**
352     * Returns whether this server socket is closed or not.
353     *
354     * @return {@code true} if this socket is closed, {@code false} otherwise.
355     */
356    public boolean isClosed() {
357        return isClosed;
358    }
359
360    private void checkOpen() throws SocketException {
361        if (isClosed()) {
362            throw new SocketException("Socket is closed");
363        }
364    }
365
366    /**
367     * Sets the value for the socket option {@code SocketOptions.SO_REUSEADDR}.
368     *
369     * @param reuse
370     *            the socket option setting.
371     * @throws SocketException
372     *             if an error occurs while setting the option value.
373     */
374    public void setReuseAddress(boolean reuse) throws SocketException {
375        checkOpen();
376        impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
377    }
378
379    /**
380     * Gets the value of the socket option {@code SocketOptions.SO_REUSEADDR}.
381     *
382     * @return {@code true} if the option is enabled, {@code false} otherwise.
383     * @throws SocketException
384     *             if an error occurs while reading the option value.
385     */
386    public boolean getReuseAddress() throws SocketException {
387        checkOpen();
388        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue();
389    }
390
391    /**
392     * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
393     */
394    public void setReceiveBufferSize(int size) throws SocketException {
395        checkOpen();
396        if (size < 1) {
397            throw new IllegalArgumentException("size < 1");
398        }
399        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
400    }
401
402    /**
403     * Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}.
404     */
405    public int getReceiveBufferSize() throws SocketException {
406        checkOpen();
407        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
408    }
409
410    /**
411     * Returns this socket's {@code ServerSocketChannel}, if one exists. A channel is
412     * available only if this socket wraps a channel. (That is, you can go from a
413     * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.)
414     * In practice, this means that the socket must have been created by
415     * {@link java.nio.channels.ServerSocketChannel#open}.
416     */
417    public ServerSocketChannel getChannel() {
418        return null;
419    }
420
421    /**
422     * Sets performance preferences for connection time, latency and bandwidth.
423     * <p>
424     * This method does currently nothing.
425     *
426     * @param connectionTime
427     *            the value representing the importance of a short connecting
428     *            time.
429     * @param latency
430     *            the value representing the importance of low latency.
431     * @param bandwidth
432     *            the value representing the importance of high bandwidth.
433     */
434    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
435        // Our socket implementation only provide one protocol: TCP/IP, so
436        // we do nothing for this method
437    }
438}
439