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