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