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