ServerSocket.java revision 051128862ae7c5c031b8ddb763848ed264a63746
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
23import org.apache.harmony.luni.net.PlainServerSocketImpl;
24import org.apache.harmony.luni.platform.Platform;
25import org.apache.harmony.luni.util.Msg;
26
27/**
28 * This class represents a server-side socket that waits for incoming client
29 * connections. A {@code ServerSocket} handles the requests and sends back an
30 * appropriate reply. The actual tasks that a server socket must accomplish are
31 * implemented by an internal {@code SocketImpl} instance.
32 */
33public class ServerSocket {
34
35    SocketImpl impl;
36
37    static SocketImplFactory factory;
38
39    private volatile boolean isCreated;
40
41    private boolean isBound;
42
43    private boolean isClosed;
44
45    static {
46        Platform.getNetworkSystem().oneTimeInitialization(true);
47    }
48
49    /**
50     * Constructs a new {@code ServerSocket} instance which is not bound to any
51     * port. The default number of pending connections may be backlogged.
52     *
53     * @throws IOException
54     *             if an error occurs while creating the server socket.
55     */
56    public ServerSocket() throws IOException {
57        impl = factory != null ? factory.createSocketImpl()
58                : new PlainServerSocketImpl();
59    }
60
61    /**
62     * Unspecified constructor.
63     *
64     * Warning: this function is technically part of API#1.
65     * Hiding it for API#2 broke source compatibility.
66     * Removing it entirely would theoretically break binary compatibility,
67     *     and would be better done with some visibility over the extent
68     *     of the compatibility breakage (expected to be non-existent).
69     *
70     * @hide
71     */
72    protected ServerSocket(SocketImpl impl) {
73        this.impl = impl;
74    }
75
76    /**
77     * Constructs a new {@code ServerSocket} instance bound to the nominated
78     * port on the localhost. The default number of pending connections may be
79     * backlogged. If {@code aport} is 0 a free port is assigned to the socket.
80     *
81     * @param aport
82     *            the port number to listen for connection requests on.
83     * @throws IOException
84     *             if an error occurs while creating the server socket.
85     */
86    public ServerSocket(int aport) throws IOException {
87        this(aport, defaultBacklog(), Inet4Address.ANY);
88    }
89
90    /**
91     * Constructs a new {@code ServerSocket} instance bound to the nominated
92     * port on the localhost. The number of pending connections that may be
93     * backlogged is specified by {@code backlog}. If {@code aport} is 0 a free
94     * port is assigned to the socket.
95     *
96     * @param aport
97     *            the port number to listen for connection requests on.
98     * @param backlog
99     *            the number of pending connection requests, before requests
100     *            will be rejected.
101     * @throws IOException
102     *             if an error occurs while creating the server socket.
103     */
104    public ServerSocket(int aport, int backlog) throws IOException {
105        this(aport, backlog, Inet4Address.ANY);
106    }
107
108    /**
109     * Constructs a new {@code ServerSocket} instance bound to the nominated
110     * local host address and port. The number of pending connections that may
111     * be backlogged is specified by {@code backlog}. If {@code aport} is 0 a
112     * free port is assigned to the socket.
113     *
114     * @param aport
115     *            the port number to listen for connection requests on.
116     * @param localAddr
117     *            the local machine address to bind on.
118     * @param backlog
119     *            the number of pending connection requests, before requests
120     *            will be rejected.
121     * @throws IOException
122     *             if an error occurs while creating the server socket.
123     */
124    public ServerSocket(int aport, int backlog, InetAddress localAddr)
125            throws IOException {
126        super();
127        checkListen(aport);
128        impl = factory != null ? factory.createSocketImpl()
129                : new PlainServerSocketImpl();
130        InetAddress addr = localAddr == null ? Inet4Address.ANY : localAddr;
131
132        synchronized (this) {
133            impl.create(true);
134            isCreated = true;
135            try {
136                impl.bind(addr, aport);
137                isBound = true;
138                impl.listen(backlog > 0 ? backlog : defaultBacklog());
139            } catch (IOException e) {
140                close();
141                throw e;
142            }
143        }
144    }
145
146    /**
147     * Waits for an incoming request and blocks until the connection is opened.
148     * This method returns a socket object representing the just opened
149     * connection.
150     *
151     * @return the connection representing socket.
152     * @throws IOException
153     *             if an error occurs while accepting a new connection.
154     */
155    public Socket accept() throws IOException {
156        checkClosedAndCreate(false);
157        if (!isBound()) {
158            throw new SocketException(Msg.getString("K031f")); //$NON-NLS-1$
159        }
160
161        Socket aSocket = new Socket();
162        try {
163            synchronized (this) {
164                implAccept(aSocket);
165            }
166            SecurityManager security = System.getSecurityManager();
167            if (security != null) {
168                security.checkAccept(aSocket.getInetAddress().getHostAddress(),
169                        aSocket.getPort());
170            }
171        } catch (SecurityException e) {
172            aSocket.close();
173            throw e;
174        } catch (IOException e) {
175            aSocket.close();
176            throw e;
177        }
178        return aSocket;
179    }
180
181    /**
182     * Checks whether the server may listen for connection requests on {@code
183     * aport}. Throws an exception if the port is outside the valid range
184     * {@code 0 <= aport <= 65535 }or does not satisfy the security policy.
185     *
186     * @param aPort
187     *            the candidate port to listen on.
188     */
189    void checkListen(int aPort) {
190        if (aPort < 0 || aPort > 65535) {
191            throw new IllegalArgumentException(Msg.getString("K0325", aPort)); //$NON-NLS-1$
192        }
193        SecurityManager security = System.getSecurityManager();
194        if (security != null) {
195            security.checkListen(aPort);
196        }
197    }
198
199    /**
200     * Closes this server socket and its implementation. Any attempt to connect
201     * to this socket thereafter will fail.
202     *
203     * @throws IOException
204     *             if an error occurs while closing this socket.
205     */
206    public void close() throws IOException {
207        isClosed = true;
208        impl.close();
209    }
210
211    /**
212     * Answer the default number of pending connections on a server socket. If
213     * the backlog value maximum is reached, any subsequent incoming request is
214     * rejected.
215     *
216     * @return int the default number of pending connection requests
217     */
218    static int defaultBacklog() {
219        return 50;
220    }
221
222    /**
223     * Gets the local IP address of this server socket or {@code null} if the
224     * socket is unbound. This is useful for multihomed hosts.
225     *
226     * @return the local address of this server socket.
227     */
228    public InetAddress getInetAddress() {
229        if (!isBound()) {
230            return null;
231        }
232        return impl.getInetAddress();
233    }
234
235    /**
236     * Gets the local port of this server socket or {@code -1} if the socket is
237     * unbound.
238     *
239     * @return the local port this server is listening on.
240     */
241    public int getLocalPort() {
242        if (!isBound()) {
243            return -1;
244        }
245        return impl.getLocalPort();
246    }
247
248    /**
249     * Gets the timeout period of this server socket. This is the time the
250     * server will wait listening for accepted connections before exiting.
251     *
252     * @return the listening timeout value of this server socket.
253     * @throws IOException
254     *             if the option cannot be retrieved.
255     */
256    public synchronized int getSoTimeout() throws IOException {
257        if (!isCreated) {
258            synchronized (this) {
259                if (!isCreated) {
260                    try {
261                        impl.create(true);
262                    } catch (SocketException e) {
263                        throw e;
264                    } catch (IOException e) {
265                        throw new SocketException(e.toString());
266                    }
267                    isCreated = true;
268                }
269            }
270        }
271        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
272    }
273
274    /**
275     * Invokes the server socket implementation to accept a connection on the
276     * given socket {@code aSocket}.
277     *
278     * @param aSocket
279     *            the concrete {@code SocketImpl} to accept the connection
280     *            request on.
281     * @throws IOException
282     *             if the connection cannot be accepted.
283     */
284    protected final void implAccept(Socket aSocket) throws IOException {
285        impl.accept(aSocket.impl);
286        aSocket.accepted();
287    }
288
289    /**
290     * Sets the server socket implementation factory of this instance. This
291     * method may only be invoked with sufficient security privilege and only
292     * once during the application lifetime.
293     *
294     * @param aFactory
295     *            the streaming socket factory to be used for further socket
296     *            instantiations.
297     * @throws IOException
298     *             if the factory could not be set or is already set.
299     */
300    public static synchronized void setSocketFactory(SocketImplFactory aFactory)
301            throws IOException {
302        SecurityManager security = System.getSecurityManager();
303        if (security != null) {
304            security.checkSetFactory();
305        }
306        if (factory != null) {
307            throw new SocketException(Msg.getString("K0042")); //$NON-NLS-1$
308        }
309        factory = aFactory;
310    }
311
312    /**
313     * Sets the timeout period of this server socket. This is the time the
314     * server will wait listening for accepted connections before exiting. This
315     * value must be a positive number.
316     *
317     * @param timeout
318     *            the listening timeout value of this server socket.
319     * @throws SocketException
320     *             if an error occurs while setting the option.
321     */
322    public synchronized void setSoTimeout(int timeout) throws SocketException {
323        checkClosedAndCreate(true);
324        if (timeout < 0) {
325            throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$
326        }
327        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
328    }
329
330    /**
331     * Returns a textual representation of this server socket including the
332     * address, port and the state. The port field is set to {@code 0} if there
333     * is no connection to the server socket.
334     *
335     * @return the textual socket representation.
336     */
337    @Override
338    public String toString() {
339        StringBuilder result = new StringBuilder(64);
340        result.append("ServerSocket["); //$NON-NLS-1$
341        if (!isBound()) {
342            return result.append("unbound]").toString(); //$NON-NLS-1$
343        }
344        return result.append("addr=") //$NON-NLS-1$
345                .append(getInetAddress().getHostName()).append("/") //$NON-NLS-1$
346                .append(getInetAddress().getHostAddress()).append(
347                        ",port=0,localport=") //$NON-NLS-1$
348                .append(getLocalPort()).append("]") //$NON-NLS-1$
349                .toString();
350    }
351
352    /**
353     * Binds this server socket to the given local socket address. The default
354     * number of pending connections may be backlogged. If the {@code localAddr}
355     * is set to {@code null} the socket will be bound to an available local
356     * address on any free port of the system.
357     *
358     * @param localAddr
359     *            the local address and port to bind on.
360     * @throws IllegalArgumentException
361     *             if the {@code SocketAddress} is not supported.
362     * @throws IOException
363     *             if the socket is already bound or a problem occurs during
364     *             binding.
365     */
366    public void bind(SocketAddress localAddr) throws IOException {
367        bind(localAddr, defaultBacklog());
368    }
369
370    /**
371     * Binds this server socket to the given local socket address. If the
372     * {@code localAddr} is set to {@code null} the socket will be bound to an
373     * available local address on any free port of the system. The value for
374     * {@code backlog} must e greater than {@code 0} otherwise the default value
375     * will be used.
376     *
377     * @param localAddr
378     *            the local machine address and port to bind on.
379     * @param backlog
380     *            the number of pending connection requests, before requests
381     *            will be rejected.
382     * @throws IllegalArgumentException
383     *             if the {@code SocketAddress} is not supported.
384     * @throws IOException
385     *             if the socket is already bound or a problem occurs during
386     *             binding.
387     */
388    public void bind(SocketAddress localAddr, int backlog) throws IOException {
389        checkClosedAndCreate(true);
390        if (isBound()) {
391            throw new BindException(Msg.getString("K0315")); //$NON-NLS-1$
392        }
393        int port = 0;
394        InetAddress addr = Inet4Address.ANY;
395        if (localAddr != null) {
396            if (!(localAddr instanceof InetSocketAddress)) {
397                throw new IllegalArgumentException(Msg.getString(
398                        "K0316", localAddr.getClass())); //$NON-NLS-1$
399            }
400            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
401            if ((addr = inetAddr.getAddress()) == null) {
402                throw new SocketException(Msg.getString(
403                        "K0317", inetAddr.getHostName())); //$NON-NLS-1$
404            }
405            port = inetAddr.getPort();
406        }
407        SecurityManager security = System.getSecurityManager();
408        if (security != null) {
409            security.checkListen(port);
410        }
411
412        synchronized (this) {
413            try {
414                impl.bind(addr, port);
415                isBound = true;
416                impl.listen(backlog > 0 ? backlog : defaultBacklog());
417            } catch (IOException e) {
418                close();
419                throw e;
420            }
421        }
422    }
423
424    /**
425     * Gets the local socket address of this server socket or {@code null} if
426     * the socket is unbound. This is useful on multihomed hosts.
427     *
428     * @return the local socket address and port this socket is bound to.
429     */
430    public SocketAddress getLocalSocketAddress() {
431        if (!isBound()) {
432            return null;
433        }
434        return new InetSocketAddress(getInetAddress(), getLocalPort());
435    }
436
437    /**
438     * Returns whether this server socket is bound to a local address and port
439     * or not.
440     *
441     * @return {@code true} if this socket is bound, {@code false} otherwise.
442     */
443    public boolean isBound() {
444        return isBound;
445    }
446
447    /**
448     * Returns whether this server socket is closed or not.
449     *
450     * @return {@code true} if this socket is closed, {@code false} otherwise.
451     */
452    public boolean isClosed() {
453        return isClosed;
454    }
455
456    /**
457     * Checks whether the socket is closed, and throws an exception.
458     */
459    private void checkClosedAndCreate(boolean create) throws SocketException {
460        if (isClosed()) {
461            throw new SocketException(Msg.getString("K003d")); //$NON-NLS-1$
462        }
463
464        if (!create || isCreated) {
465            return;
466        }
467
468        synchronized (this) {
469            if (isCreated) {
470                return;
471            }
472            try {
473                impl.create(true);
474            } catch (SocketException e) {
475                throw e;
476            } catch (IOException e) {
477                throw new SocketException(e.toString());
478            }
479            isCreated = true;
480        }
481    }
482
483    /**
484     * Sets the value for the socket option {@code SocketOptions.SO_REUSEADDR}.
485     *
486     * @param reuse
487     *            the socket option setting.
488     * @throws SocketException
489     *             if an error occurs while setting the option value.
490     */
491    public void setReuseAddress(boolean reuse) throws SocketException {
492        checkClosedAndCreate(true);
493        impl.setOption(SocketOptions.SO_REUSEADDR, reuse ? Boolean.TRUE
494                : Boolean.FALSE);
495    }
496
497    /**
498     * Gets the value of the socket option {@code SocketOptions.SO_REUSEADDR}.
499     *
500     * @return {@code true} if the option is enabled, {@code false} otherwise.
501     * @throws SocketException
502     *             if an error occurs while reading the option value.
503     */
504    public boolean getReuseAddress() throws SocketException {
505        checkClosedAndCreate(true);
506        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR))
507                .booleanValue();
508    }
509
510    /**
511     * Sets the server socket receive buffer size {@code
512     * SocketOptions.SO_RCVBUF}.
513     *
514     * @param size
515     *            the buffer size in bytes.
516     * @throws SocketException
517     *             if an error occurs while setting the size or the size is
518     *             invalid.
519     */
520    public void setReceiveBufferSize(int size) throws SocketException {
521        checkClosedAndCreate(true);
522        if (size < 1) {
523            throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$
524        }
525        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
526    }
527
528    /**
529     * Gets the value for the receive buffer size socket option {@code
530     * SocketOptions.SO_RCVBUF}.
531     *
532     * @return the receive buffer size of this socket.
533     * @throws SocketException
534     *             if an error occurs while reading the option value.
535     */
536    public int getReceiveBufferSize() throws SocketException {
537        checkClosedAndCreate(true);
538        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
539    }
540
541    /**
542     * Gets the related channel if this instance was created by a
543     * {@code ServerSocketChannel}. The current implementation returns always {@code
544     * null}.
545     *
546     * @return the related {@code ServerSocketChannel} if any.
547     */
548    public ServerSocketChannel getChannel() {
549        return null;
550    }
551
552    /**
553     * Sets performance preferences for connection time, latency and bandwidth.
554     * <p>
555     * This method does currently nothing.
556     *
557     * @param connectionTime
558     *            the value representing the importance of a short connecting
559     *            time.
560     * @param latency
561     *            the value representing the importance of low latency.
562     * @param bandwidth
563     *            the value representing the importance of high bandwidth.
564     */
565    public void setPerformancePreferences(int connectionTime, int latency,
566            int bandwidth) {
567        // Our socket implementation only provide one protocol: TCP/IP, so
568        // we do nothing for this method
569    }
570}
571