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