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