DatagramSocket.java revision 8de7cf6bff36093dda9e25a1ab3718720cb54906
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;
22
23/**
24 * This class implements a UDP socket for sending and receiving {@code
25 * DatagramPacket}. A {@code DatagramSocket} object can be used for both
26 * endpoints of a connection for a packet delivery service.
27 *
28 * @see DatagramPacket
29 * @see DatagramSocketImplFactory
30 */
31public class DatagramSocket {
32
33    DatagramSocketImpl impl;
34
35    InetAddress address;
36
37    int port = -1;
38
39    static DatagramSocketImplFactory factory;
40
41    boolean isBound = false;
42
43    private boolean isConnected = false;
44
45    private boolean isClosed = false;
46
47    private Object lock = new Object();
48
49    /**
50     * Constructs a UDP datagram socket which is bound to any available port on
51     * the localhost.
52     *
53     * @throws SocketException
54     *             if an error occurs while creating or binding the socket.
55     */
56    public DatagramSocket() throws SocketException {
57        this(0);
58    }
59
60    /**
61     * Constructs a UDP datagram socket which is bound to the specific port
62     * {@code aPort} on the localhost. Valid values for {@code aPort} are
63     * between 0 and 65535 inclusive.
64     *
65     * @param aPort
66     *            the port to bind on the localhost.
67     * @throws SocketException
68     *             if an error occurs while creating or binding the socket.
69     */
70    public DatagramSocket(int aPort) throws SocketException {
71        super();
72        checkListen(aPort);
73        createSocket(aPort, Inet4Address.ANY);
74    }
75
76    /**
77     * Constructs a UDP datagram socket which is bound to the specific local
78     * address {@code addr} on port {@code aPort}. Valid values for {@code
79     * aPort} are between 0 and 65535 inclusive.
80     *
81     * @param aPort
82     *            the port to bind on the localhost.
83     * @param addr
84     *            the address to bind on the localhost.
85     * @throws SocketException
86     *             if an error occurs while creating or binding the socket.
87     */
88    public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
89        super();
90        checkListen(aPort);
91        createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr);
92    }
93
94    private void checkListen(int aPort) {
95        if (aPort < 0 || aPort > 65535) {
96            throw new IllegalArgumentException("Port out of range: " + aPort);
97        }
98    }
99
100    /**
101     * Closes this UDP datagram socket and all possibly associated channels.
102     */
103    // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is
104    // noted as not being synchronized.
105    public void close() {
106        isClosed = true;
107        impl.close();
108    }
109
110    /**
111     * Connects this UDP datagram socket to the specific target host with the
112     * address {@code anAdress} on port {@code aPort}. The host and port are
113     * validated, thereafter the only validation on {@code send()} and {@code
114     * receive()} is to check whether the packet address/port matches the
115     * connected target.
116     *
117     * @param anAddress
118     *            the target address of this socket.
119     * @param aPort
120     *            the target port of this socket.
121     */
122    public void connect(InetAddress anAddress, int aPort) {
123        if (anAddress == null || aPort < 0 || aPort > 65535) {
124            throw new IllegalArgumentException("Address null or destination port out of range");
125        }
126
127        synchronized (lock) {
128            if (isClosed()) {
129                return;
130            }
131            try {
132                checkClosedAndBind(true);
133            } catch (SocketException e) {
134                // Ignored
135            }
136
137            try {
138                impl.connect(anAddress, aPort);
139            } catch (SocketException e) {
140                // not connected at the native level just do what we did before
141            }
142            address = anAddress;
143            port = aPort;
144            isConnected = true;
145        }
146    }
147
148    /**
149     * Disconnects this UDP datagram socket from the remote host. This method
150     * called on an unconnected socket does nothing.
151     */
152    public void disconnect() {
153        if (isClosed() || !isConnected()) {
154            return;
155        }
156        impl.disconnect();
157        address = null;
158        port = -1;
159        isConnected = false;
160    }
161
162    synchronized void createSocket(int aPort, InetAddress addr)
163            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. {@see SocketOptions#SO_BINDTODEVICE}
383     *
384     * @hide
385     */
386    public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
387        if (netInterface == null) {
388            throw new NullPointerException("networkInterface == null");
389        }
390
391        impl.setOption(SocketOptions.SO_BINDTODEVICE,
392            Integer.valueOf(netInterface.getIndex()));
393    }
394
395    /**
396     * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}.
397     */
398    public synchronized void setSendBufferSize(int size) throws SocketException {
399        if (size < 1) {
400            throw new IllegalArgumentException("size < 1");
401        }
402        checkClosedAndBind(false);
403        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
404    }
405
406    /**
407     * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
408     */
409    public synchronized void setReceiveBufferSize(int size) throws SocketException {
410        if (size < 1) {
411            throw new IllegalArgumentException("size < 1");
412        }
413        checkClosedAndBind(false);
414        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
415    }
416
417    /**
418     * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket.
419     * This receive timeout defines the period the socket will block waiting to
420     * receive data before throwing an {@code InterruptedIOException}. The value
421     * {@code 0} (default) is used to set an infinite timeout. To have effect
422     * this option must be set before the blocking method was called.
423     *
424     * @param timeout the timeout in milliseconds or 0 for no timeout.
425     * @throws SocketException
426     *                if an error occurs while setting the option.
427     */
428    public synchronized void setSoTimeout(int timeout) throws SocketException {
429        if (timeout < 0) {
430            throw new IllegalArgumentException("timeout < 0");
431        }
432        checkClosedAndBind(false);
433        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
434    }
435
436    /**
437     * Sets the socket implementation factory. This may only be invoked once
438     * over the lifetime of the application. This factory is used to create
439     * a new datagram socket implementation.
440     *
441     * @param fac
442     *            the socket factory to use.
443     * @throws IOException
444     *                if the factory has already been set.
445     * @see DatagramSocketImplFactory
446     */
447    public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
448            throws IOException {
449        if (factory != null) {
450            throw new SocketException("Factory already set");
451        }
452        factory = fac;
453    }
454
455    /**
456     * Constructs a new {@code DatagramSocket} using the specific datagram
457     * socket implementation {@code socketImpl}. The created {@code
458     * DatagramSocket} will not be bound.
459     *
460     * @param socketImpl
461     *            the DatagramSocketImpl to use.
462     */
463    protected DatagramSocket(DatagramSocketImpl socketImpl) {
464        if (socketImpl == null) {
465            throw new NullPointerException();
466        }
467        impl = socketImpl;
468    }
469
470    /**
471     * Constructs a new {@code DatagramSocket} bound to the host/port specified
472     * by the {@code SocketAddress} {@code localAddr} or an unbound {@code
473     * DatagramSocket} if the {@code SocketAddress} is {@code null}.
474     *
475     * @param localAddr
476     *            the local machine address and port to bind to.
477     * @throws IllegalArgumentException
478     *             if the SocketAddress is not supported
479     * @throws SocketException
480     *             if a problem occurs creating or binding the socket.
481     */
482    public DatagramSocket(SocketAddress localAddr) throws SocketException {
483        if (localAddr != null) {
484            if (!(localAddr instanceof InetSocketAddress)) {
485                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
486                        localAddr.getClass());
487            }
488            checkListen(((InetSocketAddress) localAddr).getPort());
489        }
490        impl = factory != null ? factory.createDatagramSocketImpl()
491                : new PlainDatagramSocketImpl();
492        impl.create();
493        if (localAddr != null) {
494            try {
495                bind(localAddr);
496            } catch (SocketException e) {
497                close();
498                throw e;
499            }
500        }
501        // SocketOptions.SO_BROADCAST is set by default for DatagramSocket
502        setBroadcast(true);
503    }
504
505    void checkClosedAndBind(boolean bind) throws SocketException {
506        if (isClosed()) {
507            throw new SocketException("Socket is closed");
508        }
509        if (bind && !isBound()) {
510            checkListen(0);
511            impl.bind(0, Inet4Address.ANY);
512            isBound = true;
513        }
514    }
515
516    /**
517     * Binds this socket to the local address and port specified by {@code
518     * localAddr}. If this value is {@code null} any free port on a valid local
519     * address is used.
520     *
521     * @param localAddr
522     *            the local machine address and port to bind on.
523     * @throws IllegalArgumentException
524     *             if the SocketAddress is not supported
525     * @throws SocketException
526     *             if the socket is already bound or a problem occurs during
527     *             binding.
528     */
529    public void bind(SocketAddress localAddr) throws SocketException {
530        checkClosedAndBind(false);
531        int localPort = 0;
532        InetAddress addr = Inet4Address.ANY;
533        if (localAddr != null) {
534            if (!(localAddr instanceof InetSocketAddress)) {
535                throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
536                        localAddr.getClass());
537            }
538            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
539            addr = inetAddr.getAddress();
540            if (addr == null) {
541                throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
542            }
543            localPort = inetAddr.getPort();
544            checkListen(localPort);
545        }
546        impl.bind(localPort, addr);
547        isBound = true;
548    }
549
550    /**
551     * Connects this datagram socket to the remote host and port specified by
552     * {@code remoteAddr}. The host and port are validated, thereafter the only
553     * validation on {@code send()} and {@code receive()} is that the packet
554     * address/port matches the connected target.
555     *
556     * @param remoteAddr
557     *            the address and port of the target host.
558     * @throws SocketException
559     *                if an error occurs during connecting.
560     */
561    public void connect(SocketAddress remoteAddr) throws SocketException {
562        if (remoteAddr == null) {
563            throw new IllegalArgumentException("remoteAddr == null");
564        }
565
566        if (!(remoteAddr instanceof InetSocketAddress)) {
567            throw new IllegalArgumentException("Remote address not an InetSocketAddress: " +
568                    remoteAddr.getClass());
569        }
570
571        InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
572        if (inetAddr.getAddress() == null) {
573            throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
574        }
575
576        synchronized (lock) {
577            // make sure the socket is open
578            checkClosedAndBind(true);
579
580            // now try to do the connection at the native level. To be
581            // compatible for the case when the address is inaddr_any we just
582            // eat the exception an act as if we are connected at the java level
583            try {
584                impl.connect(inetAddr.getAddress(), inetAddr.getPort());
585            } catch (Exception e) {
586                // not connected at the native level just do what we did before
587            }
588
589            // if we get here then we connected ok
590            address = inetAddr.getAddress();
591            port = inetAddr.getPort();
592            isConnected = true;
593        }
594    }
595
596    /**
597     * Determines whether the socket is bound to an address or not.
598     *
599     * @return {@code true} if the socket is bound, {@code false} otherwise.
600     */
601    public boolean isBound() {
602        return isBound;
603    }
604
605    /**
606     * Determines whether the socket is connected to a target host.
607     *
608     * @return {@code true} if the socket is connected, {@code false} otherwise.
609     */
610    public boolean isConnected() {
611        return isConnected;
612    }
613
614    /**
615     * Gets the address and port of the connected remote host. If this socket is
616     * not connected yet, {@code null} is returned.
617     *
618     * @return the remote socket address.
619     */
620    public SocketAddress getRemoteSocketAddress() {
621        if (!isConnected()) {
622            return null;
623        }
624        return new InetSocketAddress(getInetAddress(), getPort());
625    }
626
627    /**
628     * Gets the bound local address and port of this socket. If the socket is
629     * unbound, {@code null} is returned.
630     *
631     * @return the local socket address.
632     */
633    public SocketAddress getLocalSocketAddress() {
634        if (!isBound()) {
635            return null;
636        }
637        return new InetSocketAddress(getLocalAddress(), getLocalPort());
638    }
639
640    /**
641     * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option
642     * has to be enabled if more than one UDP socket wants to be bound to the
643     * same address. That could be needed for receiving multicast packets.
644     * <p>
645     * There is an undefined behavior if this option is set after the socket is
646     * already bound.
647     *
648     * @param reuse
649     *            the socket option value to enable or disable this option.
650     * @throws SocketException
651     *             if the socket is closed or the option could not be set.
652     */
653    public void setReuseAddress(boolean reuse) throws SocketException {
654        checkClosedAndBind(false);
655        impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
656    }
657
658    /**
659     * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}.
660     *
661     * @return {@code true} if the option is enabled, {@code false} otherwise.
662     * @throws SocketException
663     *             if the socket is closed or the option is invalid.
664     */
665    public boolean getReuseAddress() throws SocketException {
666        checkClosedAndBind(false);
667        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR))
668                .booleanValue();
669    }
670
671    /**
672     * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option
673     * must be enabled to send broadcast messages.
674     *
675     * @param broadcast
676     *            the socket option value to enable or disable this option.
677     * @throws SocketException
678     *             if the socket is closed or the option could not be set.
679     */
680    public void setBroadcast(boolean broadcast) throws SocketException {
681        checkClosedAndBind(false);
682        impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast));
683    }
684
685    /**
686     * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}.
687     *
688     * @return {@code true} if the option is enabled, {@code false} otherwise.
689     * @throws SocketException
690     *             if the socket is closed or the option is invalid.
691     */
692    public boolean getBroadcast() throws SocketException {
693        checkClosedAndBind(false);
694        return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST))
695                .booleanValue();
696    }
697
698    /**
699     * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket.
700     *
701     * @throws SocketException
702     *             if the socket is closed or the option could not be set.
703     */
704    public void setTrafficClass(int value) throws SocketException {
705        checkClosedAndBind(false);
706        if (value < 0 || value > 255) {
707            throw new IllegalArgumentException();
708        }
709        impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
710    }
711
712    /**
713     * Returns this socket's {@see SocketOptions#IP_TOS} setting.
714     *
715     * @throws SocketException
716     *             if the socket is closed or the option is invalid.
717     */
718    public int getTrafficClass() throws SocketException {
719        checkClosedAndBind(false);
720        return (Integer) impl.getOption(SocketOptions.IP_TOS);
721    }
722
723    /**
724     * Gets the state of this socket.
725     *
726     * @return {@code true} if the socket is closed, {@code false} otherwise.
727     */
728    public boolean isClosed() {
729        return isClosed;
730    }
731
732    /**
733     * Returns this socket's {@code DatagramChannel}, if one exists. A channel is
734     * available only if this socket wraps a channel. (That is, you can go from a
735     * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.)
736     * In practice, this means that the socket must have been created by
737     * {@link java.nio.channels.DatagramChannel#open}.
738     */
739    public DatagramChannel getChannel() {
740        return null;
741    }
742}
743