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