OSDN Git Service

2003-09-10 Michael Koch <konqueror@gmx.de>
[pf3gnuchains/gcc-fork.git] / libjava / java / net / DatagramSocket.java
1 /* DatagramSocket.java -- A class to model UDP sockets
2    Copyright (C) 1998, 1999, 2000, 2002, 2003 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.net;
40
41 import gnu.java.net.PlainDatagramSocketImpl;
42 import java.io.IOException;
43 import java.nio.channels.DatagramChannel;
44 import java.nio.channels.IllegalBlockingModeException;
45
46 /**
47  * Written using on-line Java Platform 1.2 API Specification, as well
48  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
49  * Status:  Believed complete and correct.
50  */
51
52 /**
53  * This class models a connectionless datagram socket that sends 
54  * individual packets of data across the network.  In the TCP/IP world,
55  * this means UDP.  Datagram packets do not have guaranteed delivery,
56  * or any guarantee about the order the data will be received on the
57  * remote host.
58  * 
59  * @author Aaron M. Renn (arenn@urbanophile.com)
60  * @author Warren Levy (warrenl@cygnus.com)
61  * @date May 3, 1999.
62  */
63
64 public class DatagramSocket
65 {
66   /**
67    * This is the user DatagramSocketImplFactory for this class.  If this
68    * variable is null, a default factory is used.
69    */
70   static DatagramSocketImplFactory factory;
71           
72   /**
73    * This is the implementation object used by this socket.
74    */
75   DatagramSocketImpl impl;
76
77   /**
78    * The unique DatagramChannel object associated with this datagram socket,
79    * or null.
80    */
81   DatagramChannel ch;
82
83   /**
84    * This is the address we are "connected" to
85    */
86   private InetAddress remoteAddress;
87
88   /**
89    * This is the port we are "connected" to
90    */
91   private int remotePort = -1;
92
93   /**
94    * Indicates when the socket is closed.
95    */
96   private boolean closed = false;
97
98   /**
99    * Creates a <code>DatagramSocket</code> from a specified 
100    * <code>DatagramSocketImpl</code> instance
101    *
102    * @param impl The <code>DatagramSocketImpl</code> the socket will be 
103    * created from
104    * 
105    * @since 1.4
106    */
107   protected DatagramSocket (DatagramSocketImpl impl)
108   {
109     this.impl = impl;
110     this.remoteAddress = null;
111     this.remotePort = -1;
112   }
113
114   /**
115    * Initializes a new instance of <code>DatagramSocket</code> that binds to 
116    * a random port and every address on the local machine.
117    *
118    * @exception SocketException If an error occurs.
119    * @exception SecurityException If a security manager exists and
120    * its <code>checkListen</code> method doesn't allow the operation.
121    */
122   public DatagramSocket() throws SocketException
123   {
124     this(0, null);
125   }
126
127   /**
128    * Initializes a new instance of <code>DatagramSocket</code> that binds to 
129    * the specified port and every address on the local machine.
130    *
131    * @param port The local port number to bind to.
132    *
133    * @exception SecurityException If a security manager exists and its
134    * <code>checkListen</code> method doesn't allow the operation.
135    * @exception SocketException If an error occurs.
136    */
137   public DatagramSocket(int port) throws SocketException
138   {
139     this(port, null);
140   }
141
142   /**
143    * Initializes a new instance of <code>DatagramSocket</code> that binds to 
144    * the specified local port and address.
145    *
146    * @param port The local port number to bind to.
147    * @param laddr The local address to bind to.
148    *
149    * @exception SecurityException If a security manager exists and its
150    * checkListen method doesn't allow the operation.
151    * @exception SocketException If an error occurs.
152    */
153   public DatagramSocket(int port, InetAddress laddr) throws SocketException
154   {
155     if (port < 0 || port > 65535)
156       throw new IllegalArgumentException("Invalid port: " + port);
157
158     SecurityManager s = System.getSecurityManager();
159     if (s != null)
160       s.checkListen(port);
161
162     String propVal = System.getProperty("impl.prefix");
163     if (propVal == null || propVal.equals(""))
164       impl = new PlainDatagramSocketImpl();
165     else
166       try
167         {
168           impl = (DatagramSocketImpl) Class.forName
169             ("java.net." + propVal + "DatagramSocketImpl").newInstance();
170         }
171       catch (Exception e)
172         {
173           System.err.println("Could not instantiate class: java.net." +
174             propVal + "DatagramSocketImpl");
175           impl = new PlainDatagramSocketImpl();
176         }
177     impl.create();
178
179     if (laddr == null)
180       laddr = InetAddress.ANY_IF;
181     
182     try
183       {
184         impl.bind (port, laddr);
185       }
186     catch (SocketException exception)
187       {
188         impl.close ();
189         throw exception;
190       }
191     catch (RuntimeException exception)
192       {
193         impl.close ();
194         throw exception;
195       }
196     catch (Error error)
197       {
198         impl.close ();
199         throw error;
200       }
201   }
202
203   /**
204    * Initializes a new instance of <code>DatagramSocket</code> that binds to 
205    * the specified local port and address.
206    *
207    * @param port The local port number to bind to.
208    * @param laddr The local address to bind to.
209    *
210    * @exception SecurityException If a security manager exists and its
211    * <code>checkListen</code> method doesn't allow the operation.
212    * @exception SocketException If an error occurs.
213    *
214    * @since 1.4
215    */
216   public DatagramSocket (SocketAddress address) throws SocketException
217   {
218     this (((InetSocketAddress) address).getPort (),
219           ((InetSocketAddress) address).getAddress ());
220   }
221   
222   /**
223    * Closes this datagram socket.
224    */
225   public void close()
226   {
227     if (!closed)
228       {
229         impl.close();
230         remoteAddress = null;
231         remotePort = -1;
232         closed = true;
233       }
234   }
235
236   /**
237    * This method returns the remote address to which this socket is 
238    * connected.  If this socket is not connected, then this method will
239    * return <code>null</code>.
240    * 
241    * @return The remote address.
242    *
243    * @since 1.2
244    */
245   public InetAddress getInetAddress()
246   {
247     return remoteAddress;
248   }
249
250   /**
251    * This method returns the remote port to which this socket is
252    * connected.  If this socket is not connected, then this method will
253    * return -1.
254    * 
255    * @return The remote port.
256    *
257    * @since 1.2
258    */
259   public int getPort()
260   {
261     return remotePort;
262   }
263
264   /**
265    * Returns the local address this datagram socket is bound to.
266    * 
267    * @since 1.1
268    */
269   public InetAddress getLocalAddress()
270   {
271     if (impl == null
272         || closed)
273       return null;
274     
275     InetAddress result;
276     
277     try
278       {
279         result = (InetAddress) impl.getOption (SocketOptions.SO_BINDADDR);
280
281         SecurityManager s = System.getSecurityManager();
282         if (s != null)
283           s.checkConnect (result.getHostName(), -1);
284       }
285     catch (SecurityException e)
286       {
287         result = InetAddress.ANY_IF;
288       }
289     catch (SocketException e)
290       {
291         result = InetAddress.ANY_IF;
292       }
293
294     return result;
295   }
296
297   /**
298    * Returns the local port this socket is bound to.
299    *
300    * @return The local port number.
301    */
302   public int getLocalPort()
303   {
304     return impl.getLocalPort();
305   }
306
307   /**
308    * Returns the value of the socket's SO_TIMEOUT setting.  If this method
309    * returns 0 then SO_TIMEOUT is disabled.
310    *
311    * @return The current timeout in milliseconds.
312    *
313    * @exception SocketException If an error occurs.
314    * 
315    * @since 1.1
316    */
317   public synchronized int getSoTimeout() throws SocketException
318   {
319     if (impl == null)
320       throw new SocketException ("Cannot initialize Socket implementation");
321
322     Object timeout = impl.getOption(SocketOptions.SO_TIMEOUT);
323
324     if (timeout instanceof Integer) 
325       return ((Integer)timeout).intValue();
326     else
327       return 0;
328   }
329
330   /**
331    * Sets the value of the socket's SO_TIMEOUT value.  A value of 0 will
332    * disable SO_TIMEOUT.  Any other value is the number of milliseconds
333    * a socket read/write will block before timing out.
334    *
335    * @param timeout The new SO_TIMEOUT value in milliseconds.
336    *
337    * @exception SocketException If an error occurs.
338    *
339    * @since 1.1
340    */
341   public synchronized void setSoTimeout(int timeout) throws SocketException
342   {
343     if (timeout < 0)
344       throw new IllegalArgumentException("Invalid timeout: " + timeout);
345
346     impl.setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout));
347   }
348
349   /**
350    * This method returns the value of the system level socket option
351    * SO_SNDBUF, which is used by the operating system to tune buffer
352    * sizes for data transfers.
353    *
354    * @return The send buffer size.
355    *
356    * @exception SocketException If an error occurs.
357    *
358    * @since 1.2
359    */
360   public int getSendBufferSize() throws SocketException
361   {
362     if (impl == null)
363       throw new SocketException ("Cannot initialize Socket implementation");
364
365     Object obj = impl.getOption(SocketOptions.SO_SNDBUF);
366
367     if (obj instanceof Integer)
368       return(((Integer)obj).intValue());
369     else
370       throw new SocketException("Unexpected type");
371   }
372
373   /**
374    * This method sets the value for the system level socket option
375    * SO_SNDBUF to the specified value.  Note that valid values for this
376    * option are specific to a given operating system.
377    *
378    * @param size The new send buffer size.
379    *
380    * @exception SocketException If an error occurs.
381    * @exception IllegalArgumentException If size is 0 or negative.
382    *
383    * @since 1.2
384    */
385   public void setSendBufferSize(int size) throws SocketException
386   {
387     if (size < 0)
388       throw new IllegalArgumentException("Buffer size is less than 0");
389   
390     impl.setOption(SocketOptions.SO_SNDBUF, new Integer(size));
391   }
392
393   /**
394    * This method returns the value of the system level socket option
395    * SO_RCVBUF, which is used by the operating system to tune buffer
396    * sizes for data transfers.
397    *
398    * @return The receive buffer size.
399    *
400    * @exception SocketException If an error occurs.
401    *
402    * @since 1.2
403    */
404   public int getReceiveBufferSize() throws SocketException
405   {
406     if (impl == null)
407       throw new SocketException ("Cannot initialize Socket implementation");
408
409     Object obj = impl.getOption(SocketOptions.SO_RCVBUF);
410   
411     if (obj instanceof Integer)
412       return(((Integer)obj).intValue());
413     else 
414       throw new SocketException("Unexpected type");
415   }
416
417   /**
418    * This method sets the value for the system level socket option
419    * SO_RCVBUF to the specified value.  Note that valid values for this
420    * option are specific to a given operating system.
421    *
422    * @param size The new receive buffer size.
423    *
424    * @exception SocketException If an error occurs.
425    * @exception IllegalArgumentException If size is 0 or negative.
426    * 
427    * @since 1.2
428    */
429   public void setReceiveBufferSize(int size) throws SocketException
430   {
431     if (impl == null)
432       throw new SocketException ("Cannot initialize Socket implementation");
433
434     if (size < 0)
435       throw new IllegalArgumentException("Buffer size is less than 0");
436
437     impl.setOption(SocketOptions.SO_RCVBUF, new Integer(size));
438   }
439
440   /**
441    * This method connects this socket to the specified address and port.
442    * When a datagram socket is connected, it will only send or receive
443    * packets to and from the host to which it is connected. A multicast
444    * socket that is connected may only send and not receive packets.
445    * 
446    * @param address The address to connect this socket to.
447    * @param port The port to connect this socket to.
448    *
449    * @exception SocketException If an error occurs.
450    * @exception IllegalArgumentException If address or port are invalid.
451    * @exception SecurityException If the caller is not allowed to send
452    * datagrams to or receive from this address and port.
453    *
454    * @since 1.2
455    */
456   public void connect(InetAddress address, int port)
457   {
458     if (address == null)
459       throw new IllegalArgumentException ("Connect address may not be null");
460
461     if ((port < 1) || (port > 65535))
462       throw new IllegalArgumentException ("Port number is illegal: " + port);
463
464     SecurityManager sm = System.getSecurityManager();
465     if (sm != null)
466       sm.checkConnect(address.getHostName(), port);
467
468     try
469       {
470         impl.connect (address, port);
471         remoteAddress = address;
472         remotePort = port;
473       }
474     catch (SocketException e)
475       {
476         // This means simply not connected or connect not implemented.
477       }
478   }
479
480   /**
481    * This method disconnects this socket from the address/port it was
482    * connected to.  If the socket was not connected in the first place,
483    * this method does nothing.
484    * 
485    * @since 1.2
486    */
487   public void disconnect()
488   {
489     impl.disconnect();
490     remoteAddress = null;
491     remotePort = -1;
492   }
493
494   /**
495    * Reads a datagram packet from the socket.  Note that this method
496    * will block until a packet is received from the network.  On return,
497    * the passed in <code>DatagramPacket</code> is populated with the data
498    * received and all the other information about the packet.
499    *
500    * @param p A <code>DatagramPacket</code> for storing the data
501    *
502    * @exception IOException If an error occurs.
503    * @exception SocketTimeoutException If setSoTimeout was previously called
504    * and the timeout has expired.
505    * @exception PortUnreachableException If the socket is connected to a
506    * currently unreachable destination. Note, there is no guarantee that the
507    * exception will be thrown.
508    * @exception IllegalBlockingModeException If this socket has an associated
509    * channel, and the channel is in non-blocking mode.
510    * @exception SecurityException If a security manager exists and its
511    * checkAccept ethod doesn't allow the receive.
512    */
513   public synchronized void receive(DatagramPacket p) throws IOException
514   {
515     if (impl == null)
516       throw new IOException ("Cannot initialize Socket implementation");
517
518     if (remoteAddress != null && remoteAddress.isMulticastAddress ())
519       throw new IOException (
520         "Socket connected to a multicast address my not receive");
521
522     if (ch != null && !ch.isBlocking ())
523       throw new IllegalBlockingModeException ();
524
525     impl.receive(p);
526
527     SecurityManager s = System.getSecurityManager();
528     if (s != null && isConnected ())
529       s.checkAccept (p.getAddress().getHostName (), p.getPort ());
530   }
531
532   /**
533    * Sends the specified packet.  The host and port to which the packet
534    * are to be sent should be set inside the packet.
535    *
536    * @param p The datagram packet to send.
537    *
538    * @exception IOException If an error occurs.
539    * @exception SecurityException If a security manager exists and its
540    * checkMulticast or checkConnect method doesn't allow the send.
541    * @exception PortUnreachableException If the socket is connected to a
542    * currently unreachable destination. Note, there is no guarantee that the
543    * exception will be thrown.
544    * @exception IllegalBlockingModeException If this socket has an associated
545    * channel, and the channel is in non-blocking mode.
546    */
547   public void send(DatagramPacket p) throws IOException
548   {
549     // JDK1.2: Don't do security checks if socket is connected; see jdk1.2 api.
550     SecurityManager s = System.getSecurityManager();
551     if (s != null && !isConnected ())
552       {
553         InetAddress addr = p.getAddress();
554         if (addr.isMulticastAddress())
555           s.checkMulticast(addr);
556         else
557           s.checkConnect(addr.getHostAddress(), p.getPort());
558       }
559
560     if (isConnected ())
561       {
562         if (p.getAddress () != null && (remoteAddress != p.getAddress () ||
563                                         remotePort != p.getPort ()))
564           throw new IllegalArgumentException (
565             "DatagramPacket address does not match remote address" );
566       }
567             
568     // FIXME: if this is a subclass of MulticastSocket,
569     // use getTimeToLive for TTL val.
570
571     if (ch != null && !ch.isBlocking ())
572       throw new IllegalBlockingModeException ();
573
574     impl.send(p);
575   }
576
577   /**
578    * Binds the socket to the given socket address.
579    *
580    * @param address The socket address to bind to.
581    *
582    * @exception SocketException If an error occurs.
583    * @exception SecurityException If a security manager exists and
584    * its checkListen method doesn't allow the operation.
585    * @exception IllegalArgumentException If address type is not supported.
586    *
587    * @since 1.4
588    */
589   public void bind (SocketAddress address)
590     throws SocketException
591   {
592     if (! (address instanceof InetSocketAddress))
593       throw new IllegalArgumentException ();
594
595     InetSocketAddress tmp = (InetSocketAddress) address;
596
597     SecurityManager s = System.getSecurityManager ();
598     if (s != null)
599       s.checkListen(tmp.getPort ());
600
601     impl.bind (tmp.getPort (), tmp.getAddress ());
602   }
603
604   /**
605    * Checks if the datagram socket is closed.
606    *
607    * @since 1.4
608    */
609   public boolean isClosed()
610   {
611     return closed;
612   }
613
614   /**
615    * Returns the datagram channel assoziated with this datagram socket.
616    * 
617    * @since 1.4
618    */
619   public DatagramChannel getChannel()
620   {
621     return ch;
622   }
623
624   /**
625    * Connects the datagram socket to a specified socket address.
626    *
627    * @param address The socket address to connect to.
628    *
629    * @exception SocketException If an error occurs.
630    * @exception IllegalArgumentException If address type is not supported.
631    *
632    * @since 1.4
633    */
634   public void connect (SocketAddress address) throws SocketException
635   {
636     if ( !(address instanceof InetSocketAddress) )
637       throw new IllegalArgumentException (
638                       "SocketAddress is not InetSocketAddress");
639
640     InetSocketAddress tmp = (InetSocketAddress) address;
641     connect( tmp.getAddress(), tmp.getPort());
642   }
643   
644   /**
645    * Returns the binding state of the socket.
646    * 
647    * @since 1.4
648    */
649   public boolean isBound()
650   {
651     try
652       {
653         Object bindaddr = impl.getOption (SocketOptions.SO_BINDADDR);
654       }
655     catch (SocketException e)
656       {
657         return false;
658       }
659
660     return true;
661   }
662
663   /**
664    * Returns the connection state of the socket.
665    * 
666    * @since 1.4
667    */
668   public boolean isConnected()
669   {
670     return remoteAddress != null;
671   }
672
673   /**
674    * Returns the SocketAddress of the host this socket is conneted to
675    * or null if this socket is not connected.
676    * 
677    * @since 1.4
678    */
679   public SocketAddress getRemoteSocketAddress()
680   {
681     if (!isConnected ())
682       return null;
683
684     return new InetSocketAddress (remoteAddress, remotePort);
685   }
686
687   /**
688    * Returns the local SocketAddress this socket is bound to
689    * or null if it is not bound.
690    * 
691    * @since 1.4
692    */
693   public SocketAddress getLocalSocketAddress()
694   {
695     InetAddress addr;
696     
697     try
698       {
699         addr = (InetAddress) impl.getOption (SocketOptions.SO_BINDADDR);
700       }
701     catch (SocketException e)
702       {
703         return null;
704       }
705
706     return new InetSocketAddress (addr, impl.localPort);
707   }
708
709   /**
710    * Enables/Disables SO_REUSEADDR.
711    *
712    * @param on Whether or not to have SO_REUSEADDR turned on.
713    *
714    * @exception SocketException If an error occurs.
715    *
716    * @since 1.4
717    */
718   public void setReuseAddress(boolean on) throws SocketException
719   {
720     if (impl == null)
721       throw new SocketException ("Cannot initialize Socket implementation");
722
723     impl.setOption (SocketOptions.SO_REUSEADDR, new Boolean (on));
724   }
725
726   /**
727    * Checks if SO_REUSEADDR is enabled.
728    *
729    * @exception SocketException If an error occurs.
730    * 
731    * @since 1.4
732    */
733   public boolean getReuseAddress() throws SocketException
734   {
735     if (impl == null)
736       throw new SocketException ("Cannot initialize Socket implementation");
737
738     Object obj = impl.getOption (SocketOptions.SO_REUSEADDR);
739   
740     if (obj instanceof Boolean)
741       return(((Boolean) obj).booleanValue ());
742     else 
743       throw new SocketException ("Unexpected type");
744   }
745
746   /**
747    * Enables/Disables SO_BROADCAST
748    * 
749    * @param on Whether or not to have SO_BROADCAST turned on
750    *
751    * @exception SocketException If an error occurs
752    *
753    * @since 1.4
754    */
755   public void setBroadcast(boolean on) throws SocketException
756   {
757     if (impl == null)
758       throw new SocketException ("Cannot initialize Socket implementation");
759
760     impl.setOption (SocketOptions.SO_BROADCAST, new Boolean (on));
761   }
762
763   /**
764    * Checks if SO_BROADCAST is enabled
765    * 
766    * @exception SocketException If an error occurs
767    * 
768    * @since 1.4
769    */
770   public boolean getBroadcast() throws SocketException
771   {
772     if (impl == null)
773       throw new SocketException ("Cannot initialize Socket implementation");
774
775     Object obj = impl.getOption (SocketOptions.SO_BROADCAST);
776   
777     if (obj instanceof Boolean)
778       return ((Boolean) obj).booleanValue ();
779     else 
780       throw new SocketException ("Unexpected type");
781   }
782
783   /**
784    * Sets the traffic class value
785    *
786    * @param tc The traffic class
787    *
788    * @exception SocketException If an error occurs
789    * @exception IllegalArgumentException If tc value is illegal
790    *
791    * @see DatagramSocket#getTrafficClass()
792    * 
793    * @since 1.4
794    */
795   public void setTrafficClass(int tc)
796     throws SocketException
797   {
798     if (impl == null)
799       throw new SocketException ("Cannot initialize Socket implementation");
800
801     if (tc < 0 || tc > 255)
802       throw new IllegalArgumentException();
803
804     impl.setOption (SocketOptions.IP_TOS, new Integer (tc));
805   }
806   
807   /**
808    * Returns the current traffic class
809    * 
810    * @see DatagramSocket#setTrafficClass(int tc)
811    *
812    * @exception SocketException If an error occurs
813    * 
814    * @since 1.4
815    */
816   public int getTrafficClass() throws SocketException
817   {
818     if (impl == null)
819       throw new SocketException( "Cannot initialize Socket implementation");
820
821     Object obj = impl.getOption(SocketOptions.IP_TOS);
822
823     if (obj instanceof Integer)
824       return ((Integer) obj).intValue ();
825     else
826       throw new SocketException ("Unexpected type");
827   }
828   
829   /**
830    * Sets the datagram socket implementation factory for the application
831    *
832    * @param fac The factory to set
833    *
834    * @exception IOException If an error occurs
835    * @exception SocketException If the factory is already defined
836    * @exception SecurityException If a security manager exists and its
837    * checkSetFactory method doesn't allow the operation
838    */
839   public static void setDatagramSocketImplFactory
840     (DatagramSocketImplFactory fac) throws IOException
841   {
842     if (factory != null)
843       throw new SocketException ("DatagramSocketImplFactory already defined");
844
845     SecurityManager sm = System.getSecurityManager();
846     if (sm != null)
847       sm.checkSetFactory();
848
849     factory = fac;
850   }
851 }