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