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