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