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