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