DatagramSocket.java revision 8ecba5fd3f3ab30aa57caf917963bed50087ea9c
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 pack.resetLengthForReceive(); 253 impl.receive(pack); 254 } 255 256 /** 257 * Sends a packet over this socket. 258 * 259 * @param pack 260 * the {@code DatagramPacket} which has to be sent. 261 * @throws IOException 262 * if an error occurs while sending the packet. 263 */ 264 public void send(DatagramPacket pack) throws IOException { 265 checkOpen(); 266 ensureBound(); 267 268 InetAddress packAddr = pack.getAddress(); 269 if (address != null) { // The socket is connected 270 if (packAddr != null) { 271 if (!address.equals(packAddr) || port != pack.getPort()) { 272 throw new IllegalArgumentException("Packet address mismatch with connected address"); 273 } 274 } else { 275 pack.setAddress(address); 276 pack.setPort(port); 277 } 278 } else { 279 // not connected so the target address is not allowed to be null 280 if (packAddr == null) { 281 throw new NullPointerException("Destination address is null"); 282 } 283 } 284 impl.send(pack); 285 } 286 287 /** 288 * Sets the network interface used by this socket. Any packets sent 289 * via this socket are transmitted via the specified interface. Any 290 * packets received by this socket will come from the specified 291 * interface. Broadcast datagrams received on this interface will 292 * be processed by this socket. This corresponds to Linux's SO_BINDTODEVICE. 293 * 294 * @hide used by GoogleTV for DHCP 295 */ 296 public void setNetworkInterface(NetworkInterface netInterface) throws SocketException { 297 if (netInterface == null) { 298 throw new NullPointerException("networkInterface == null"); 299 } 300 try { 301 Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName()); 302 } catch (ErrnoException errnoException) { 303 throw errnoException.rethrowAsSocketException(); 304 } 305 } 306 307 /** 308 * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}. 309 */ 310 public synchronized void setSendBufferSize(int size) throws SocketException { 311 if (size < 1) { 312 throw new IllegalArgumentException("size < 1"); 313 } 314 checkOpen(); 315 impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); 316 } 317 318 /** 319 * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}. 320 */ 321 public synchronized void setReceiveBufferSize(int size) throws SocketException { 322 if (size < 1) { 323 throw new IllegalArgumentException("size < 1"); 324 } 325 checkOpen(); 326 impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); 327 } 328 329 /** 330 * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket. 331 * This receive timeout defines the period the socket will block waiting to 332 * receive data before throwing an {@code InterruptedIOException}. The value 333 * {@code 0} (default) is used to set an infinite timeout. To have effect 334 * this option must be set before the blocking method was called. 335 * 336 * @param timeout the timeout in milliseconds or 0 for no timeout. 337 * @throws SocketException 338 * if an error occurs while setting the option. 339 */ 340 public synchronized void setSoTimeout(int timeout) throws SocketException { 341 if (timeout < 0) { 342 throw new IllegalArgumentException("timeout < 0"); 343 } 344 checkOpen(); 345 impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); 346 } 347 348 /** 349 * Sets the socket implementation factory. This may only be invoked once 350 * over the lifetime of the application. This factory is used to create 351 * a new datagram socket implementation. 352 * 353 * @param fac 354 * the socket factory to use. 355 * @throws IOException 356 * if the factory has already been set. 357 * @see DatagramSocketImplFactory 358 */ 359 public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) 360 throws IOException { 361 if (factory != null) { 362 throw new SocketException("Factory already set"); 363 } 364 factory = fac; 365 } 366 367 /** 368 * Constructs a new {@code DatagramSocket} using the specific datagram 369 * socket implementation {@code socketImpl}. The created {@code 370 * DatagramSocket} will not be bound. 371 * 372 * @param socketImpl 373 * the DatagramSocketImpl to use. 374 */ 375 protected DatagramSocket(DatagramSocketImpl socketImpl) { 376 if (socketImpl == null) { 377 throw new NullPointerException(); 378 } 379 impl = socketImpl; 380 } 381 382 /** 383 * Constructs a new {@code DatagramSocket} bound to the host/port specified 384 * by the {@code SocketAddress} {@code localAddr} or an unbound {@code 385 * DatagramSocket} if the {@code SocketAddress} is {@code null}. 386 * 387 * @param localAddr 388 * the local machine address and port to bind to. 389 * @throws IllegalArgumentException 390 * if the SocketAddress is not supported 391 * @throws SocketException 392 * if a problem occurs creating or binding the socket. 393 */ 394 public DatagramSocket(SocketAddress localAddr) throws SocketException { 395 if (localAddr != null) { 396 if (!(localAddr instanceof InetSocketAddress)) { 397 throw new IllegalArgumentException("Local address not an InetSocketAddress: " + 398 localAddr.getClass()); 399 } 400 checkPort(((InetSocketAddress) localAddr).getPort()); 401 } 402 impl = factory != null ? factory.createDatagramSocketImpl() 403 : new PlainDatagramSocketImpl(); 404 impl.create(); 405 if (localAddr != null) { 406 try { 407 bind(localAddr); 408 } catch (SocketException e) { 409 close(); 410 throw e; 411 } 412 } 413 // SocketOptions.SO_BROADCAST is set by default for DatagramSocket 414 setBroadcast(true); 415 } 416 417 void checkOpen() throws SocketException { 418 if (isClosed()) { 419 throw new SocketException("Socket is closed"); 420 } 421 } 422 423 private void ensureBound() throws SocketException { 424 if (!isBound()) { 425 impl.bind(0, Inet4Address.ANY); 426 isBound = true; 427 } 428 } 429 430 /** 431 * Binds this socket to the local address and port specified by {@code 432 * localAddr}. If this value is {@code null} any free port on a valid local 433 * address is used. 434 * 435 * @param localAddr 436 * the local machine address and port to bind on. 437 * @throws IllegalArgumentException 438 * if the SocketAddress is not supported 439 * @throws SocketException 440 * if the socket is already bound or a problem occurs during 441 * binding. 442 */ 443 public void bind(SocketAddress localAddr) throws SocketException { 444 checkOpen(); 445 int localPort = 0; 446 InetAddress addr = Inet4Address.ANY; 447 if (localAddr != null) { 448 if (!(localAddr instanceof InetSocketAddress)) { 449 throw new IllegalArgumentException("Local address not an InetSocketAddress: " + 450 localAddr.getClass()); 451 } 452 InetSocketAddress inetAddr = (InetSocketAddress) localAddr; 453 addr = inetAddr.getAddress(); 454 if (addr == null) { 455 throw new SocketException("Host is unresolved: " + inetAddr.getHostName()); 456 } 457 localPort = inetAddr.getPort(); 458 checkPort(localPort); 459 } 460 impl.bind(localPort, addr); 461 isBound = true; 462 } 463 464 /** 465 * Connects this datagram socket to the address and port specified by {@code peer}. 466 * Future calls to {@link #send} will use this as the default target, and {@link #receive} 467 * will only accept packets from this source. 468 * 469 * @throws SocketException if an error occurs. 470 */ 471 public void connect(SocketAddress peer) throws SocketException { 472 if (peer == null) { 473 throw new IllegalArgumentException("peer == null"); 474 } 475 476 if (!(peer instanceof InetSocketAddress)) { 477 throw new IllegalArgumentException("peer not an InetSocketAddress: " + peer.getClass()); 478 } 479 480 InetSocketAddress isa = (InetSocketAddress) peer; 481 if (isa.getAddress() == null) { 482 throw new SocketException("Host is unresolved: " + isa.getHostName()); 483 } 484 485 synchronized (lock) { 486 checkOpen(); 487 ensureBound(); 488 489 this.address = isa.getAddress(); 490 this.port = isa.getPort(); 491 this.isConnected = true; 492 493 impl.connect(address, port); 494 } 495 } 496 497 /** 498 * Connects this datagram socket to the specific {@code address} and {@code port}. 499 * Future calls to {@link #send} will use this as the default target, and {@link #receive} 500 * will only accept packets from this source. 501 * 502 * <p>Beware: because it can't throw, this method silently ignores failures. 503 * Use {@link #connect(SocketAddress)} instead. 504 */ 505 public void connect(InetAddress address, int port) { 506 if (address == null) { 507 throw new IllegalArgumentException("address == null"); 508 } 509 try { 510 connect(new InetSocketAddress(address, port)); 511 } catch (SocketException connectException) { 512 // TODO: or just use SneakyThrow? There's a clear API bug here. 513 pendingConnectException = connectException; 514 } 515 } 516 517 /** 518 * Returns true if this socket is bound to a local address. See {@link #bind}. 519 */ 520 public boolean isBound() { 521 return isBound; 522 } 523 524 /** 525 * Returns true if this datagram socket is connected to a remote address. See {@link #connect}. 526 */ 527 public boolean isConnected() { 528 return isConnected; 529 } 530 531 /** 532 * Returns the {@code SocketAddress} this socket is connected to, or null for an unconnected 533 * socket. 534 */ 535 public SocketAddress getRemoteSocketAddress() { 536 if (!isConnected()) { 537 return null; 538 } 539 return new InetSocketAddress(getInetAddress(), getPort()); 540 } 541 542 /** 543 * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket. 544 */ 545 public SocketAddress getLocalSocketAddress() { 546 if (!isBound()) { 547 return null; 548 } 549 return new InetSocketAddress(getLocalAddress(), getLocalPort()); 550 } 551 552 /** 553 * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option 554 * has to be enabled if more than one UDP socket wants to be bound to the 555 * same address. That could be needed for receiving multicast packets. 556 * <p> 557 * There is an undefined behavior if this option is set after the socket is 558 * already bound. 559 * 560 * @param reuse 561 * the socket option value to enable or disable this option. 562 * @throws SocketException 563 * if the socket is closed or the option could not be set. 564 */ 565 public void setReuseAddress(boolean reuse) throws SocketException { 566 checkOpen(); 567 impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse)); 568 } 569 570 /** 571 * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}. 572 * 573 * @return {@code true} if the option is enabled, {@code false} otherwise. 574 * @throws SocketException 575 * if the socket is closed or the option is invalid. 576 */ 577 public boolean getReuseAddress() throws SocketException { 578 checkOpen(); 579 return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue(); 580 } 581 582 /** 583 * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option 584 * must be enabled to send broadcast messages. 585 * 586 * @param broadcast 587 * the socket option value to enable or disable this option. 588 * @throws SocketException 589 * if the socket is closed or the option could not be set. 590 */ 591 public void setBroadcast(boolean broadcast) throws SocketException { 592 checkOpen(); 593 impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast)); 594 } 595 596 /** 597 * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}. 598 * 599 * @return {@code true} if the option is enabled, {@code false} otherwise. 600 * @throws SocketException 601 * if the socket is closed or the option is invalid. 602 */ 603 public boolean getBroadcast() throws SocketException { 604 checkOpen(); 605 return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST)).booleanValue(); 606 } 607 608 /** 609 * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket. 610 * 611 * @throws SocketException 612 * if the socket is closed or the option could not be set. 613 */ 614 public void setTrafficClass(int value) throws SocketException { 615 checkOpen(); 616 if (value < 0 || value > 255) { 617 throw new IllegalArgumentException(); 618 } 619 impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); 620 } 621 622 /** 623 * Returns this socket's {@see SocketOptions#IP_TOS} setting. 624 * 625 * @throws SocketException 626 * if the socket is closed or the option is invalid. 627 */ 628 public int getTrafficClass() throws SocketException { 629 checkOpen(); 630 return (Integer) impl.getOption(SocketOptions.IP_TOS); 631 } 632 633 /** 634 * Gets the state of this socket. 635 * 636 * @return {@code true} if the socket is closed, {@code false} otherwise. 637 */ 638 public boolean isClosed() { 639 return isClosed; 640 } 641 642 /** 643 * Returns this socket's {@code DatagramChannel}, if one exists. A channel is 644 * available only if this socket wraps a channel. (That is, you can go from a 645 * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.) 646 * In practice, this means that the socket must have been created by 647 * {@link java.nio.channels.DatagramChannel#open}. 648 */ 649 public DatagramChannel getChannel() { 650 return null; 651 } 652 653 /** 654 * @hide internal use only 655 */ 656 public final FileDescriptor getFileDescriptor$() { 657 return impl.fd; 658 } 659} 660