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