DatagramSocket.java revision 8de7cf6bff36093dda9e25a1ab3718720cb54906
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 super(); 72 checkListen(aPort); 73 createSocket(aPort, Inet4Address.ANY); 74 } 75 76 /** 77 * Constructs a UDP datagram socket which is bound to the specific local 78 * address {@code addr} on port {@code aPort}. Valid values for {@code 79 * aPort} are between 0 and 65535 inclusive. 80 * 81 * @param aPort 82 * the port to bind on the localhost. 83 * @param addr 84 * the address to bind on the localhost. 85 * @throws SocketException 86 * if an error occurs while creating or binding the socket. 87 */ 88 public DatagramSocket(int aPort, InetAddress addr) throws SocketException { 89 super(); 90 checkListen(aPort); 91 createSocket(aPort, (addr == null) ? Inet4Address.ANY : addr); 92 } 93 94 private void checkListen(int aPort) { 95 if (aPort < 0 || aPort > 65535) { 96 throw new IllegalArgumentException("Port out of range: " + aPort); 97 } 98 } 99 100 /** 101 * Closes this UDP datagram socket and all possibly associated channels. 102 */ 103 // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is 104 // noted as not being synchronized. 105 public void close() { 106 isClosed = true; 107 impl.close(); 108 } 109 110 /** 111 * Connects this UDP datagram socket to the specific target host with the 112 * address {@code anAdress} on port {@code aPort}. The host and port are 113 * validated, thereafter the only validation on {@code send()} and {@code 114 * receive()} is to check whether the packet address/port matches the 115 * connected target. 116 * 117 * @param anAddress 118 * the target address of this socket. 119 * @param aPort 120 * the target port of this socket. 121 */ 122 public void connect(InetAddress anAddress, int aPort) { 123 if (anAddress == null || aPort < 0 || aPort > 65535) { 124 throw new IllegalArgumentException("Address null or destination port out of range"); 125 } 126 127 synchronized (lock) { 128 if (isClosed()) { 129 return; 130 } 131 try { 132 checkClosedAndBind(true); 133 } catch (SocketException e) { 134 // Ignored 135 } 136 137 try { 138 impl.connect(anAddress, aPort); 139 } catch (SocketException e) { 140 // not connected at the native level just do what we did before 141 } 142 address = anAddress; 143 port = aPort; 144 isConnected = true; 145 } 146 } 147 148 /** 149 * Disconnects this UDP datagram socket from the remote host. This method 150 * called on an unconnected socket does nothing. 151 */ 152 public void disconnect() { 153 if (isClosed() || !isConnected()) { 154 return; 155 } 156 impl.disconnect(); 157 address = null; 158 port = -1; 159 isConnected = false; 160 } 161 162 synchronized void createSocket(int aPort, InetAddress addr) 163 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. {@see SocketOptions#SO_BINDTODEVICE} 383 * 384 * @hide 385 */ 386 public void setNetworkInterface(NetworkInterface netInterface) throws SocketException { 387 if (netInterface == null) { 388 throw new NullPointerException("networkInterface == null"); 389 } 390 391 impl.setOption(SocketOptions.SO_BINDTODEVICE, 392 Integer.valueOf(netInterface.getIndex())); 393 } 394 395 /** 396 * Sets this socket's {@link SocketOptions#SO_SNDBUF send buffer size}. 397 */ 398 public synchronized void setSendBufferSize(int size) throws SocketException { 399 if (size < 1) { 400 throw new IllegalArgumentException("size < 1"); 401 } 402 checkClosedAndBind(false); 403 impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); 404 } 405 406 /** 407 * Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}. 408 */ 409 public synchronized void setReceiveBufferSize(int size) throws SocketException { 410 if (size < 1) { 411 throw new IllegalArgumentException("size < 1"); 412 } 413 checkClosedAndBind(false); 414 impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); 415 } 416 417 /** 418 * Sets the {@link SocketOptions#SO_TIMEOUT read timeout} in milliseconds for this socket. 419 * This receive timeout defines the period the socket will block waiting to 420 * receive data before throwing an {@code InterruptedIOException}. The value 421 * {@code 0} (default) is used to set an infinite timeout. To have effect 422 * this option must be set before the blocking method was called. 423 * 424 * @param timeout the timeout in milliseconds or 0 for no timeout. 425 * @throws SocketException 426 * if an error occurs while setting the option. 427 */ 428 public synchronized void setSoTimeout(int timeout) throws SocketException { 429 if (timeout < 0) { 430 throw new IllegalArgumentException("timeout < 0"); 431 } 432 checkClosedAndBind(false); 433 impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); 434 } 435 436 /** 437 * Sets the socket implementation factory. This may only be invoked once 438 * over the lifetime of the application. This factory is used to create 439 * a new datagram socket implementation. 440 * 441 * @param fac 442 * the socket factory to use. 443 * @throws IOException 444 * if the factory has already been set. 445 * @see DatagramSocketImplFactory 446 */ 447 public static synchronized void setDatagramSocketImplFactory(DatagramSocketImplFactory fac) 448 throws IOException { 449 if (factory != null) { 450 throw new SocketException("Factory already set"); 451 } 452 factory = fac; 453 } 454 455 /** 456 * Constructs a new {@code DatagramSocket} using the specific datagram 457 * socket implementation {@code socketImpl}. The created {@code 458 * DatagramSocket} will not be bound. 459 * 460 * @param socketImpl 461 * the DatagramSocketImpl to use. 462 */ 463 protected DatagramSocket(DatagramSocketImpl socketImpl) { 464 if (socketImpl == null) { 465 throw new NullPointerException(); 466 } 467 impl = socketImpl; 468 } 469 470 /** 471 * Constructs a new {@code DatagramSocket} bound to the host/port specified 472 * by the {@code SocketAddress} {@code localAddr} or an unbound {@code 473 * DatagramSocket} if the {@code SocketAddress} is {@code null}. 474 * 475 * @param localAddr 476 * the local machine address and port to bind to. 477 * @throws IllegalArgumentException 478 * if the SocketAddress is not supported 479 * @throws SocketException 480 * if a problem occurs creating or binding the socket. 481 */ 482 public DatagramSocket(SocketAddress localAddr) throws SocketException { 483 if (localAddr != null) { 484 if (!(localAddr instanceof InetSocketAddress)) { 485 throw new IllegalArgumentException("Local address not an InetSocketAddress: " + 486 localAddr.getClass()); 487 } 488 checkListen(((InetSocketAddress) localAddr).getPort()); 489 } 490 impl = factory != null ? factory.createDatagramSocketImpl() 491 : new PlainDatagramSocketImpl(); 492 impl.create(); 493 if (localAddr != null) { 494 try { 495 bind(localAddr); 496 } catch (SocketException e) { 497 close(); 498 throw e; 499 } 500 } 501 // SocketOptions.SO_BROADCAST is set by default for DatagramSocket 502 setBroadcast(true); 503 } 504 505 void checkClosedAndBind(boolean bind) throws SocketException { 506 if (isClosed()) { 507 throw new SocketException("Socket is closed"); 508 } 509 if (bind && !isBound()) { 510 checkListen(0); 511 impl.bind(0, Inet4Address.ANY); 512 isBound = true; 513 } 514 } 515 516 /** 517 * Binds this socket to the local address and port specified by {@code 518 * localAddr}. If this value is {@code null} any free port on a valid local 519 * address is used. 520 * 521 * @param localAddr 522 * the local machine address and port to bind on. 523 * @throws IllegalArgumentException 524 * if the SocketAddress is not supported 525 * @throws SocketException 526 * if the socket is already bound or a problem occurs during 527 * binding. 528 */ 529 public void bind(SocketAddress localAddr) throws SocketException { 530 checkClosedAndBind(false); 531 int localPort = 0; 532 InetAddress addr = Inet4Address.ANY; 533 if (localAddr != null) { 534 if (!(localAddr instanceof InetSocketAddress)) { 535 throw new IllegalArgumentException("Local address not an InetSocketAddress: " + 536 localAddr.getClass()); 537 } 538 InetSocketAddress inetAddr = (InetSocketAddress) localAddr; 539 addr = inetAddr.getAddress(); 540 if (addr == null) { 541 throw new SocketException("Host is unresolved: " + inetAddr.getHostName()); 542 } 543 localPort = inetAddr.getPort(); 544 checkListen(localPort); 545 } 546 impl.bind(localPort, addr); 547 isBound = true; 548 } 549 550 /** 551 * Connects this datagram socket to the remote host and port specified by 552 * {@code remoteAddr}. The host and port are validated, thereafter the only 553 * validation on {@code send()} and {@code receive()} is that the packet 554 * address/port matches the connected target. 555 * 556 * @param remoteAddr 557 * the address and port of the target host. 558 * @throws SocketException 559 * if an error occurs during connecting. 560 */ 561 public void connect(SocketAddress remoteAddr) throws SocketException { 562 if (remoteAddr == null) { 563 throw new IllegalArgumentException("remoteAddr == null"); 564 } 565 566 if (!(remoteAddr instanceof InetSocketAddress)) { 567 throw new IllegalArgumentException("Remote address not an InetSocketAddress: " + 568 remoteAddr.getClass()); 569 } 570 571 InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr; 572 if (inetAddr.getAddress() == null) { 573 throw new SocketException("Host is unresolved: " + inetAddr.getHostName()); 574 } 575 576 synchronized (lock) { 577 // make sure the socket is open 578 checkClosedAndBind(true); 579 580 // now try to do the connection at the native level. To be 581 // compatible for the case when the address is inaddr_any we just 582 // eat the exception an act as if we are connected at the java level 583 try { 584 impl.connect(inetAddr.getAddress(), inetAddr.getPort()); 585 } catch (Exception e) { 586 // not connected at the native level just do what we did before 587 } 588 589 // if we get here then we connected ok 590 address = inetAddr.getAddress(); 591 port = inetAddr.getPort(); 592 isConnected = true; 593 } 594 } 595 596 /** 597 * Determines whether the socket is bound to an address or not. 598 * 599 * @return {@code true} if the socket is bound, {@code false} otherwise. 600 */ 601 public boolean isBound() { 602 return isBound; 603 } 604 605 /** 606 * Determines whether the socket is connected to a target host. 607 * 608 * @return {@code true} if the socket is connected, {@code false} otherwise. 609 */ 610 public boolean isConnected() { 611 return isConnected; 612 } 613 614 /** 615 * Gets the address and port of the connected remote host. If this socket is 616 * not connected yet, {@code null} is returned. 617 * 618 * @return the remote socket address. 619 */ 620 public SocketAddress getRemoteSocketAddress() { 621 if (!isConnected()) { 622 return null; 623 } 624 return new InetSocketAddress(getInetAddress(), getPort()); 625 } 626 627 /** 628 * Gets the bound local address and port of this socket. If the socket is 629 * unbound, {@code null} is returned. 630 * 631 * @return the local socket address. 632 */ 633 public SocketAddress getLocalSocketAddress() { 634 if (!isBound()) { 635 return null; 636 } 637 return new InetSocketAddress(getLocalAddress(), getLocalPort()); 638 } 639 640 /** 641 * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option 642 * has to be enabled if more than one UDP socket wants to be bound to the 643 * same address. That could be needed for receiving multicast packets. 644 * <p> 645 * There is an undefined behavior if this option is set after the socket is 646 * already bound. 647 * 648 * @param reuse 649 * the socket option value to enable or disable this option. 650 * @throws SocketException 651 * if the socket is closed or the option could not be set. 652 */ 653 public void setReuseAddress(boolean reuse) throws SocketException { 654 checkClosedAndBind(false); 655 impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse)); 656 } 657 658 /** 659 * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}. 660 * 661 * @return {@code true} if the option is enabled, {@code false} otherwise. 662 * @throws SocketException 663 * if the socket is closed or the option is invalid. 664 */ 665 public boolean getReuseAddress() throws SocketException { 666 checkClosedAndBind(false); 667 return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)) 668 .booleanValue(); 669 } 670 671 /** 672 * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option 673 * must be enabled to send broadcast messages. 674 * 675 * @param broadcast 676 * the socket option value to enable or disable this option. 677 * @throws SocketException 678 * if the socket is closed or the option could not be set. 679 */ 680 public void setBroadcast(boolean broadcast) throws SocketException { 681 checkClosedAndBind(false); 682 impl.setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(broadcast)); 683 } 684 685 /** 686 * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}. 687 * 688 * @return {@code true} if the option is enabled, {@code false} otherwise. 689 * @throws SocketException 690 * if the socket is closed or the option is invalid. 691 */ 692 public boolean getBroadcast() throws SocketException { 693 checkClosedAndBind(false); 694 return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST)) 695 .booleanValue(); 696 } 697 698 /** 699 * Sets the {@see SocketOptions#IP_TOS} value for every packet sent by this socket. 700 * 701 * @throws SocketException 702 * if the socket is closed or the option could not be set. 703 */ 704 public void setTrafficClass(int value) throws SocketException { 705 checkClosedAndBind(false); 706 if (value < 0 || value > 255) { 707 throw new IllegalArgumentException(); 708 } 709 impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); 710 } 711 712 /** 713 * Returns this socket's {@see SocketOptions#IP_TOS} setting. 714 * 715 * @throws SocketException 716 * if the socket is closed or the option is invalid. 717 */ 718 public int getTrafficClass() throws SocketException { 719 checkClosedAndBind(false); 720 return (Integer) impl.getOption(SocketOptions.IP_TOS); 721 } 722 723 /** 724 * Gets the state of this socket. 725 * 726 * @return {@code true} if the socket is closed, {@code false} otherwise. 727 */ 728 public boolean isClosed() { 729 return isClosed; 730 } 731 732 /** 733 * Returns this socket's {@code DatagramChannel}, if one exists. A channel is 734 * available only if this socket wraps a channel. (That is, you can go from a 735 * channel to a socket and back again, but you can't go from an arbitrary socket to a channel.) 736 * In practice, this means that the socket must have been created by 737 * {@link java.nio.channels.DatagramChannel#open}. 738 */ 739 public DatagramChannel getChannel() { 740 return null; 741 } 742} 743