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