DatagramSocket.java revision 71ad1ba183f0cad222cb80a11b2f8afc03362416
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.FileDescriptor;
21import java.io.IOException;
22import java.nio.channels.DatagramChannel;
23import libcore.io.ErrnoException;
24import libcore.io.Libcore;
25import static libcore.io.OsConstants.*;
26
27/**
28 * This class implements a UDP socket for sending and receiving {@code
29 * DatagramPacket}. A {@code DatagramSocket} object can be used for both
30 * endpoints of a connection for a packet delivery service.
31 *
32 * @see DatagramPacket
33 * @see DatagramSocketImplFactory
34 */
35public class DatagramSocket {
36
37    DatagramSocketImpl impl;
38
39    InetAddress address;
40
41    int port = -1;
42
43    static DatagramSocketImplFactory factory;
44
45    boolean isBound = false;
46
47    private boolean isConnected = false;
48
49    private SocketException pendingConnectException;
50
51    private boolean isClosed = false;
52
53    private Object lock = new Object();
54
55    /**
56     * Constructs a UDP datagram socket which is bound to any available port on
57     * the localhost.
58     *
59     * @throws SocketException
60     *             if an error occurs while creating or binding the socket.
61     */
62    public DatagramSocket() throws SocketException {
63        this(0);
64    }
65
66    /**
67     * Constructs a UDP datagram socket which is bound to the specific port
68     * {@code aPort} on the localhost. Valid values for {@code aPort} are
69     * between 0 and 65535 inclusive.
70     *
71     * @param aPort
72     *            the port to bind on the localhost.
73     * @throws SocketException
74     *             if an error occurs while creating or binding the socket.
75     */
76    public DatagramSocket(int aPort) throws SocketException {
77        checkPort(aPort);
78        createSocket(aPort, Inet4Address.ANY);
79    }
80
81    /**
82     * Constructs a UDP datagram socket which is bound to the specific local
83     * address {@code addr} on port {@code aPort}. Valid values for {@code
84     * aPort} are between 0 and 65535 inclusive.
85     *
86     * @param aPort
87     *            the port to bind on the localhost.
88     * @param addr
89     *            the address to bind on the localhost.
90     * @throws SocketException
91     *             if an error occurs while creating or binding the socket.
92     */
93    public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
94        checkPort(aPort);
95        createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
96    }
97
98    private void checkPort(int aPort) {
99        if (aPort < 0 || aPort > 65535) {
100            throw new IllegalArgumentException("Port out of range: " + aPort);
101        }
102    }
103
104    /**
105     * Closes this UDP datagram socket and all possibly associated channels.
106     */
107    // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is
108    // noted as not being synchronized.
109    public void close() {
110        isClosed = true;
111        impl.close();
112    }
113
114    /**
115     * Disconnects this UDP datagram socket from the remote host. This method
116     * called on an unconnected socket does nothing.
117     */
118    public void disconnect() {
119        if (isClosed() || !isConnected()) {
120            return;
121        }
122        impl.disconnect();
123        address = null;
124        port = -1;
125        isConnected = false;
126    }
127
128    synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
129        impl = factory != null ? factory.createDatagramSocketImpl()
130                : new PlainDatagramSocketImpl();
131        impl.create();
132        try {
133            impl.bind(aPort, addr);
134            isBound = true;
135        } catch (SocketException e) {
136            close();
137            throw e;
138        }
139    }
140
141    /**
142     * Gets the {@code InetAddress} instance representing the remote address to
143     * which this UDP datagram socket is connected.
144     *
145     * @return the remote address this socket is connected to or {@code null} if
146     *         this socket is not connected.
147     */
148    public InetAddress getInetAddress() {
149        return address;
150    }
151
152    /**
153     * Gets the {@code InetAddress} instance representing the bound local
154     * address of this UDP datagram socket.
155     *
156     * @return the local address to which this socket is bound to or {@code
157     *         null} if this socket is closed.
158     */
159    public InetAddress getLocalAddress() {
160        if (isClosed()) {
161            return null;
162        }
163        if (!isBound()) {
164            return Inet4Address.ANY;
165        }
166        return impl.getLocalAddress();
167    }
168
169    /**
170     * Gets the local port which this socket is bound to.
171     *
172     * @return the local port of this socket or {@code -1} if this socket is
173     *         closed and {@code 0} if it is unbound.
174     */
175    public int getLocalPort() {
176        if (isClosed()) {
177            return -1;
178        }
179        if (!isBound()) {
180            return 0;
181        }
182        return impl.getLocalPort();
183    }
184
185    /**
186     * Gets the remote port which this socket is connected to.
187     *
188     * @return the remote port of this socket. The return value {@code -1}
189     *         indicates that this socket is not connected.
190     */
191    public int getPort() {
192        return port;
193    }
194
195    /**
196     * Indicates whether this socket is multicast or not.
197     *
198     * @return the return value is always {@code false}.
199     */
200    boolean isMulticastSocket() {
201        return false;
202    }
203
204    /**
205     * Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}.
206     */
207    public synchronized int getReceiveBufferSize() throws SocketException {
208        checkOpen();
209        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
210    }
211
212    /**
213     * Returns this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
214     */
215    public synchronized int getSendBufferSize() throws SocketException {
216        checkOpen();
217        return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
218    }
219
220    /**
221     * Gets the socket {@link SocketOptions#SO_TIMEOUT receive timeout}.
222     *
223     * @throws SocketException
224     *                if an error occurs while getting the option value.
225     */
226    public synchronized int getSoTimeout() throws SocketException {
227        checkOpen();
228        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
229    }
230
231    /**
232     * Receives a packet from this socket and stores it in the argument {@code
233     * pack}. All fields of {@code pack} must be set according to the data
234     * received. If the received data is longer than the packet buffer size it
235     * is truncated. This method blocks until a packet is received or a timeout
236     * has expired.
237     *
238     * @param pack
239     *            the {@code DatagramPacket} to store the received data.
240     * @throws IOException
241     *                if an error occurs while receiving the packet.
242     */
243    public synchronized void receive(DatagramPacket pack) throws IOException {
244        checkOpen();
245        ensureBound();
246        if (pack == null) {
247            throw new NullPointerException();
248        }
249        if (pendingConnectException != null) {
250            throw new SocketException("Pending connect failure", pendingConnectException);
251        }
252
253        pack.setLength(pack.getCapacity());
254        impl.receive(pack);
255        // pack's length field is now updated by native code call;
256        // pack's capacity field is unchanged
257    }
258
259    /**
260     * Sends a packet over this socket.
261     *
262     * @param pack
263     *            the {@code DatagramPacket} which has to be sent.
264     * @throws IOException
265     *                if an error occurs while sending the packet.
266     */
267    public void send(DatagramPacket pack) throws IOException {
268        checkOpen();
269        ensureBound();
270
271        InetAddress packAddr = pack.getAddress();
272        if (address != null) { // The socket is connected
273            if (packAddr != null) {
274                if (!address.equals(packAddr) || port != pack.getPort()) {
275                    throw new IllegalArgumentException("Packet address mismatch with connected address");
276                }
277            } else {
278                pack.setAddress(address);
279                pack.setPort(port);
280            }
281        } else {
282            // not connected so the target address is not allowed to be null
283            if (packAddr == null) {
284                throw new NullPointerException("Destination address is null");
285            }
286        }
287        impl.send(pack);
288    }
289
290    /**
291     * Sets the network interface used by this socket.  Any packets sent
292     * via this socket are transmitted via the specified interface.  Any
293     * packets received by this socket will come from the specified
294     * interface.  Broadcast datagrams received on this interface will
295     * be processed by this socket. This corresponds to Linux's SO_BINDTODEVICE.
296     *
297     * @hide used by GoogleTV for DHCP
298     */
299    public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
300        if (netInterface == null) {
301            throw new NullPointerException("networkInterface == null");
302        }
303        try {
304            Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
305        } catch (ErrnoException errnoException) {
306            throw errnoException.rethrowAsSocketException();
307        }
308    }
309
310    /**
311     * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
312     */
313    public synchronized void setSendBufferSize(int size) throws SocketException {
314        if (size < 1) {
315            throw new IllegalArgumentException("size < 1");
316        }
317        checkOpen();
318        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
319    }
320
321    /**
322     * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
323     */
324    public synchronized void setReceiveBufferSize(int size) throws SocketException {
325        if (size < 1) {
326            throw new IllegalArgumentException("size < 1");
327        }
328        checkOpen();
329        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
330    }
331
332    /**
333     * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket.
334     * This receive timeout defines the period the socket will block waiting to
335     * receive data before throwing an {@code InterruptedIOException}. The value
336     * {@code 0} (default) is used to set an infinite timeout. To have effect
337     * this option must be set before the blocking method was called.
338     *
339     * @param timeout the timeout in milliseconds or 0 for no timeout.
340     * @throws SocketException
341     *                if an error occurs while setting the option.
342     */
343    public synchronized void setSoTimeout(int timeout) throws SocketException {
344        if (timeout < 0) {
345            throw new IllegalArgumentException("timeout < 0");
346        }
347        checkOpen();
348        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
349    }
350
351    /**
352     * Sets the socket implementation factory. This may only be invoked once
353     * over the lifetime of the application. This factory is used to create
354     * a new datagram socket implementation.
355     *
356     * @param fac
357     *            the socket factory to use.
358     * @throws IOException
359     *                if the factory has already been set.
360     * @see DatagramSocketImplFactory
361     */
362    public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
363            throws IOException {
364        if (factory != null) {
365            throw new SocketException("Factory already set");
366        }
367        factory = fac;
368    }
369
370    /**
371     * Constructs a new {@code DatagramSocket} using the specific datagram
372     * socket implementation {@code socketImpl}. The created {@code
373     * DatagramSocket} will not be bound.
374     *
375     * @param socketImpl
376     *            the DatagramSocketImpl to use.
377     */
378    protected DatagramSocket(DatagramSocketImpl socketImpl) {
379        if (socketImpl == null) {
380            throw new NullPointerException();
381        }
382        impl = socketImpl;
383    }
384
385    /**
386     * Constructs a new {@code DatagramSocket} bound to the host/port specified
387     * by the {@code SocketAddress} {@code localAddr} or an unbound {@code
388     * DatagramSocket} if the {@code SocketAddress} is {@code null}.
389     *
390     * @param localAddr
391     *            the local machine address and port to bind to.
392     * @throws IllegalArgumentException
393     *             if the SocketAddress is not supported
394     * @throws SocketException
395     *             if a problem occurs creating or binding the socket.
396     */
397    public DatagramSocket(SocketAddress localAddr) throws SocketException {
398        if (localAddr != null) {
399            if (!(localAddr instanceof InetSocketAddress)) {
400                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
401                        localAddr.getClass());
402            }
403            checkPort(((InetSocketAddress) localAddr).getPort());
404        }
405        impl = factory != null ? factory.createDatagramSocketImpl()
406                : new PlainDatagramSocketImpl();
407        impl.create();
408        if (localAddr != null) {
409            try {
410                bind(localAddr);
411            } catch (SocketException e) {
412                close();
413                throw e;
414            }
415        }
416        // SocketOptions.SO_BROADCAST is set by default for DatagramSocket
417        setBroadcast(true);
418    }
419
420    void checkOpen() throws SocketException {
421        if (isClosed()) {
422            throw new SocketException("Socket is closed");
423        }
424    }
425
426    private void ensureBound() throws SocketException {
427        if (!isBound()) {
428            impl.bind(0, Inet4Address.ANY);
429            isBound = true;
430        }
431    }
432
433    /**
434     * Binds this socket to the local address and port specified by {@code
435     * localAddr}. If this value is {@code null} any free port on a valid local
436     * address is used.
437     *
438     * @param localAddr
439     *            the local machine address and port to bind on.
440     * @throws IllegalArgumentException
441     *             if the SocketAddress is not supported
442     * @throws SocketException
443     *             if the socket is already bound or a problem occurs during
444     *             binding.
445     */
446    public void bind(SocketAddress localAddr) throws SocketException {
447        checkOpen();
448        int localPort = 0;
449        InetAddress addr = Inet4Address.ANY;
450        if (localAddr != null) {
451            if (!(localAddr instanceof InetSocketAddress)) {
452                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
453                        localAddr.getClass());
454            }
455            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
456            addr = inetAddr.getAddress();
457            if (addr == null) {
458                throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
459            }
460            localPort = inetAddr.getPort();
461            checkPort(localPort);
462        }
463        impl.bind(localPort, addr);
464        isBound = true;
465    }
466
467    /**
468     * Connects this datagram socket to the address and port specified by {@code peer}.
469     * Future calls to {@link #send} will use this as the default target, and {@link #receive}
470     * will only accept packets from this source.
471     *
472     * @throws SocketException if an error occurs.
473     */
474    public void connect(SocketAddress peer) throws SocketException {
475        if (peer == null) {
476            throw new IllegalArgumentException("peer == null");
477        }
478
479        if (!(peer instanceof InetSocketAddress)) {
480            throw new IllegalArgumentException("peer not an InetSocketAddress: " + peer.getClass());
481        }
482
483        InetSocketAddress isa = (InetSocketAddress) peer;
484        if (isa.getAddress() == null) {
485            throw new SocketException("Host is unresolved: " + isa.getHostName());
486        }
487
488        synchronized (lock) {
489            checkOpen();
490            ensureBound();
491
492            this.address = isa.getAddress();
493            this.port = isa.getPort();
494            this.isConnected = true;
495
496            impl.connect(address, port);
497        }
498    }
499
500    /**
501     * Connects this datagram socket to the specific {@code address} and {@code port}.
502     * Future calls to {@link #send} will use this as the default target, and {@link #receive}
503     * will only accept packets from this source.
504     *
505     * <p>Beware: because it can't throw, this method silently ignores failures.
506     * Use {@link #connect(SocketAddress)} instead.
507     */
508    public void connect(InetAddress address, int port) {
509        if (address == null) {
510            throw new IllegalArgumentException("address == null");
511        }
512        try {
513            connect(new InetSocketAddress(address, port));
514        } catch (SocketException connectException) {
515            // TODO: or just use SneakyThrow? There's a clear API bug here.
516            pendingConnectException = connectException;
517        }
518    }
519
520    /**
521     * Returns true if this socket is bound to a local address. See {@link #bind}.
522     */
523    public boolean isBound() {
524        return isBound;
525    }
526
527    /**
528     * Returns true if this datagram socket is connected to a remote address. See {@link #connect}.
529     */
530    public boolean isConnected() {
531        return isConnected;
532    }
533
534    /**
535     * Returns the {@code SocketAddress} this socket is connected to, or null for an unconnected
536     * socket.
537     */
538    public SocketAddress getRemoteSocketAddress() {
539        if (!isConnected()) {
540            return null;
541        }
542        return new InetSocketAddress(getInetAddress(), getPort());
543    }
544
545    /**
546     * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket.
547     */
548    public SocketAddress getLocalSocketAddress() {
549        if (!isBound()) {
550            return null;
551        }
552        return new InetSocketAddress(getLocalAddress(), getLocalPort());
553    }
554
555    /**
556     * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option
557     * has to be enabled if more than one UDP socket wants to be bound to the
558     * same address. That could be needed for receiving multicast packets.
559     * <p>
560     * There is an undefined behavior if this option is set after the socket is
561     * already bound.
562     *
563     * @param reuse
564     *            the socket option value to enable or disable this option.
565     * @throws SocketException
566     *             if the socket is closed or the option could not be set.
567     */
568    public void setReuseAddress(boolean reuse) throws SocketException {
569        checkOpen();
570        impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
571    }
572
573    /**
574     * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}.
575     *
576     * @return {@code true} if the option is enabled, {@code false} otherwise.
577     * @throws SocketException
578     *             if the socket is closed or the option is invalid.
579     */
580    public boolean getReuseAddress() throws SocketException {
581        checkOpen();
582        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue();
583    }
584
585    /**
586     * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option
587     * must be enabled to send broadcast messages.
588     *
589     * @param broadcast
590     *            the socket option value to enable or disable this option.
591     * @throws SocketException
592     *             if the socket is closed or the option could not be set.
593     */
594    public void setBroadcast(boolean broadcast) throws SocketException {
595        checkOpen();
596        impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast));
597    }
598
599    /**
600     * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}.
601     *
602     * @return {@code true} if the option is enabled, {@code false} otherwise.
603     * @throws SocketException
604     *             if the socket is closed or the option is invalid.
605     */
606    public boolean getBroadcast() throws SocketException {
607        checkOpen();
608        return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST)).booleanValue();
609    }
610
611    /**
612     * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket.
613     *
614     * @throws SocketException
615     *             if the socket is closed or the option could not be set.
616     */
617    public void setTrafficClass(int value) throws SocketException {
618        checkOpen();
619        if (value < 0 || value > 255) {
620            throw new IllegalArgumentException();
621        }
622        impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
623    }
624
625    /**
626     * Returns this socket's {@see SocketOptions#IP_TOS} setting.
627     *
628     * @throws SocketException
629     *             if the socket is closed or the option is invalid.
630     */
631    public int getTrafficClass() throws SocketException {
632        checkOpen();
633        return (Integer) impl.getOption(SocketOptions.IP_TOS);
634    }
635
636    /**
637     * Gets the state of this socket.
638     *
639     * @return {@code true} if the socket is closed, {@code false} otherwise.
640     */
641    public boolean isClosed() {
642        return isClosed;
643    }
644
645    /**
646     * Returns this socket's {@code DatagramChannel}, if one exists. A channel is
647     * available only if this socket wraps a channel. (That is, you can go from a
648     * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.)
649     * In practice, this means that the socket must have been created by
650     * {@link java.nio.channels.DatagramChannel#open}.
651     */
652    public DatagramChannel getChannel() {
653        return null;
654    }
655
656    /**
657     * @hide internal use only
658     */
659    public final FileDescriptor getFileDescriptor$() {
660        return impl.fd;
661    }
662}
663