1 /* PlainSocketImpl.java -- Default socket implementation 2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 3 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20 02110-1301 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 39 40 package gnu.java.net; 41 42 import gnu.java.nio.SocketChannelImpl; 43 import gnu.java.nio.VMChannel; 44 45 import java.io.InputStream; 46 import java.io.IOException; 47 import java.io.InterruptedIOException; 48 import java.io.OutputStream; 49 import java.net.InetAddress; 50 import java.net.InetSocketAddress; 51 import java.net.SocketAddress; 52 import java.net.SocketException; 53 import java.net.SocketImpl; 54 import java.net.SocketTimeoutException; 55 import java.nio.ByteBuffer; 56 57 /** 58 * Written using on-line Java Platform 1.2 API Specification, as well 59 * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). 60 * Status: Believed complete and correct. 61 */ 62 63 /** 64 * Unless the application installs its own SocketImplFactory, this is the 65 * default socket implemetation that will be used. It simply uses a 66 * combination of Java and native routines to implement standard BSD 67 * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM 68 * 69 * @author Per Bothner (bothner@cygnus.com) 70 * @author Nic Ferrier (nferrier@tapsellferrier.co.uk) 71 * @author Aaron M. Renn (arenn@urbanophile.com) 72 */ 73 public class PlainSocketImpl extends SocketImpl 74 { 75 76 /** 77 * The underlying plain socket VM implementation. 78 */ 79 protected VMPlainSocketImpl impl; 80 81 /** 82 * A cached copy of the in stream for reading from the socket. 83 */ 84 private InputStream in; 85 86 /** 87 * A cached copy of the out stream for writing to the socket. 88 */ 89 private OutputStream out; 90 91 /** 92 * Indicates whether a channel initiated whatever operation 93 * is being invoked on this socket. 94 */ 95 private boolean inChannelOperation; 96 97 /** 98 * The socket channel we use for IO operation. Package-private for 99 * use by inner classes. 100 */ 101 SocketChannelImpl channel; 102 103 /** 104 * Indicates whether we should ignore whether any associated 105 * channel is set to non-blocking mode. Certain operations 106 * throw an <code>IllegalBlockingModeException</code> if the 107 * associated channel is in non-blocking mode, <i>except</i> 108 * if the operation is invoked by the channel itself. 109 */ isInChannelOperation()110 public final boolean isInChannelOperation() 111 { 112 return inChannelOperation; 113 } 114 115 /** 116 * Sets our indicator of whether an I/O operation is being 117 * initiated by a channel. 118 */ setInChannelOperation(boolean b)119 public final void setInChannelOperation(boolean b) 120 { 121 inChannelOperation = b; 122 } 123 124 /** 125 * Default do nothing constructor. 126 */ PlainSocketImpl()127 public PlainSocketImpl() 128 { 129 this.impl = new VMPlainSocketImpl(); 130 } 131 132 /** 133 * Sets the specified option on a socket to the passed in object. For 134 * options that take an integer argument, the passed in object is an 135 * Integer. The option_id parameter is one of the defined constants in 136 * this interface. 137 * 138 * @param optionId The identifier of the option 139 * @param value The value to set the option to 140 * 141 * @throws SocketException if an error occurs 142 */ setOption(int optionId, Object value)143 public void setOption(int optionId, Object value) throws SocketException 144 { 145 switch (optionId) 146 { 147 case SO_LINGER: 148 case IP_MULTICAST_LOOP: 149 case SO_BROADCAST: 150 case SO_KEEPALIVE: 151 case SO_OOBINLINE: 152 case TCP_NODELAY: 153 case IP_TOS: 154 case SO_RCVBUF: 155 case SO_SNDBUF: 156 case SO_TIMEOUT: 157 case SO_REUSEADDR: 158 impl.setOption(optionId, value); 159 return; 160 default: 161 throw new SocketException("Unrecognized TCP option: " + optionId); 162 } 163 } 164 165 /** 166 * Returns the current setting of the specified option. The Object returned 167 * will be an Integer for options that have integer values. The option_id 168 * is one of the defined constants in this interface. 169 * 170 * @param optionId the option identifier 171 * 172 * @return the current value of the option 173 * 174 * @throws SocketException if an error occurs 175 */ getOption(int optionId)176 public Object getOption(int optionId) throws SocketException 177 { 178 if (optionId == SO_BINDADDR) 179 { 180 try 181 { 182 return channel.getVMChannel().getLocalAddress().getAddress(); 183 } 184 catch (IOException ioe) 185 { 186 SocketException se = new SocketException(); 187 se.initCause(ioe); 188 throw se; 189 } 190 } 191 192 // This filters options which are invalid for TCP. 193 switch (optionId) 194 { 195 case SO_LINGER: 196 case IP_MULTICAST_LOOP: 197 case SO_BROADCAST: 198 case SO_KEEPALIVE: 199 case SO_OOBINLINE: 200 case TCP_NODELAY: 201 case IP_TOS: 202 case SO_RCVBUF: 203 case SO_SNDBUF: 204 case SO_TIMEOUT: 205 case SO_REUSEADDR: 206 return impl.getOption(optionId); 207 default: 208 throw new SocketException("Unrecognized TCP option: " + optionId); 209 } 210 211 } 212 shutdownInput()213 public void shutdownInput() throws IOException 214 { 215 impl.shutdownInput(); 216 } 217 shutdownOutput()218 public void shutdownOutput() throws IOException 219 { 220 impl.shutdownOutput(); 221 } 222 223 /** 224 * Creates a new socket that is not bound to any local address/port and 225 * is not connected to any remote address/port. The stream parameter will be 226 * ignored since PlainSocketImpl always is a stream socket. Datagram sockets 227 * are handled by PlainDatagramSocketImpl. 228 * 229 * @param stream <code>true</code> for stream sockets, <code>false</code> for 230 * datagram sockets 231 */ create(boolean stream)232 protected synchronized void create(boolean stream) throws IOException 233 { 234 channel = new SocketChannelImpl(false); 235 VMChannel vmchannel = channel.getVMChannel(); 236 vmchannel.initSocket(stream); 237 channel.configureBlocking(true); 238 impl.getState().setChannelFD(vmchannel.getState()); 239 } 240 241 /** 242 * Connects to the remote hostname and port specified as arguments. 243 * 244 * @param hostname the remote hostname to connect to 245 * @param port the remote port to connect to 246 * 247 * @throws IOException If an error occurs 248 */ connect(String hostname, int port)249 protected synchronized void connect(String hostname, int port) 250 throws IOException 251 { 252 connect(InetAddress.getByName(hostname), port); 253 } 254 255 /** 256 * Connects to the remote address and port specified as arguments. 257 * 258 * @param addr the remote address to connect to 259 * @param port the remote port to connect to 260 * 261 * @throws IOException If an error occurs 262 */ connect(InetAddress addr, int port)263 protected void connect(InetAddress addr, int port) throws IOException 264 { 265 connect(new InetSocketAddress(addr, port), 0); 266 } 267 268 /** 269 * Connects to the remote socket address with a specified timeout. 270 * 271 * @param address the remote address to connect to 272 * @param timeout the timeout to use for this connect, 0 means infinite. 273 * 274 * @throws IOException If an error occurs 275 */ connect(SocketAddress address, int timeout)276 protected synchronized void connect(SocketAddress address, int timeout) 277 throws IOException 278 { 279 if (channel == null) 280 create(true); 281 boolean connected = channel.connect(address, timeout); 282 if (!connected) 283 throw new SocketTimeoutException("connect timed out"); 284 285 // Using the given SocketAddress is important to preserve 286 // hostnames given by the caller. 287 InetSocketAddress addr = (InetSocketAddress) address; 288 this.address = addr.getAddress(); 289 this.port = addr.getPort(); 290 } 291 292 /** 293 * Binds to the specified port on the specified addr. Note that this addr 294 * must represent a local IP address. **** How bind to INADDR_ANY? **** 295 * 296 * @param addr the address to bind to 297 * @param port the port number to bind to 298 * 299 * @throws IOException if an error occurs 300 */ bind(InetAddress addr, int port)301 protected synchronized void bind(InetAddress addr, int port) 302 throws IOException 303 { 304 if (channel == null) 305 create(true); 306 impl.bind(new InetSocketAddress(addr, port)); 307 localport = channel.getVMChannel().getLocalAddress().getPort(); 308 } 309 310 /** 311 * Starts listening for connections on a socket. The queuelen parameter 312 * is how many pending connections will queue up waiting to be serviced 313 * before being accept'ed. If the queue of pending requests exceeds this 314 * number, additional connections will be refused. 315 * 316 * @param queuelen The length of the pending connection queue 317 * 318 * @throws IOException If an error occurs 319 */ listen(int queuelen)320 protected synchronized void listen(int queuelen) 321 throws IOException 322 { 323 impl.listen(queuelen); 324 } 325 326 /** 327 * Accepts a new connection on this socket and returns in in the 328 * passed in SocketImpl. 329 * 330 * @param impl The SocketImpl object to accept this connection. 331 */ accept(SocketImpl impl)332 protected synchronized void accept(SocketImpl impl) 333 throws IOException 334 { 335 if (channel == null) 336 create(true); 337 if (!(impl instanceof PlainSocketImpl)) 338 throw new IOException("incompatible SocketImpl: " 339 + impl.getClass().getName()); 340 PlainSocketImpl that = (PlainSocketImpl) impl; 341 VMChannel c = channel.getVMChannel().accept(); 342 that.impl.getState().setChannelFD(c.getState()); 343 that.channel = new SocketChannelImpl(c); 344 that.setOption(SO_REUSEADDR, Boolean.TRUE); 345 // Reset the inherited timeout. 346 that.setOption(SO_TIMEOUT, Integer.valueOf(0)); 347 348 } 349 350 /** 351 * Returns the number of bytes that the caller can read from this socket 352 * without blocking. 353 * 354 * @return the number of readable bytes before blocking 355 * 356 * @throws IOException if an error occurs 357 */ available()358 protected int available() throws IOException 359 { 360 if (channel == null) 361 throw new SocketException("not connected"); 362 return channel.getVMChannel().available(); 363 } 364 365 /** 366 * Closes the socket. This will cause any InputStream or OutputStream 367 * objects for this Socket to be closed as well. 368 * 369 * <p> 370 * Note that if the SO_LINGER option is set on this socket, then the 371 * operation could block. 372 * </p> 373 * 374 * @throws IOException if an error occurs 375 */ close()376 protected void close() throws IOException 377 { 378 if (impl.getState().isValid()) 379 impl.close(); 380 381 address = null; 382 port = -1; 383 } 384 sendUrgentData(int data)385 public void sendUrgentData(int data) throws IOException 386 { 387 impl.sendUrgentData(data); 388 } 389 390 /** 391 * Returns an InputStream object for reading from this socket. This will 392 * be an instance of SocketInputStream. 393 * 394 * @return An input stream attached to the socket. 395 * 396 * @exception IOException If an error occurs 397 */ getInputStream()398 protected synchronized InputStream getInputStream() throws IOException 399 { 400 if (in == null) 401 in = new SocketInputStream(); 402 403 return in; 404 } 405 406 /** 407 * Returns an OutputStream object for writing to this socket. This will 408 * be an instance of SocketOutputStream. 409 * 410 * @return An output stream attached to the socket. 411 * 412 * @exception IOException If an error occurs 413 */ getOutputStream()414 protected synchronized OutputStream getOutputStream() throws IOException 415 { 416 if (out == null) 417 out = new SocketOutputStream(); 418 419 return out; 420 } 421 getVMChannel()422 public VMChannel getVMChannel() 423 { 424 if (channel == null) 425 return null; 426 return channel.getVMChannel(); 427 } 428 429 /* (non-Javadoc) 430 * @see java.net.SocketImpl#getInetAddress() 431 */ getInetAddress()432 protected InetAddress getInetAddress() 433 { 434 if (channel == null) 435 return null; 436 437 try 438 { 439 InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); 440 if (remote == null) 441 return null; 442 // To mimic behavior of the RI the InetAddress instance which was 443 // used to establish the connection is returned instead of one that 444 // was created by the native layer (this preserves exact hostnames). 445 if (address != null) 446 return address; 447 448 return remote.getAddress(); 449 } 450 catch (IOException ioe) 451 { 452 return null; 453 } 454 } 455 456 /* (non-Javadoc) 457 * @see java.net.SocketImpl#getLocalPort() 458 */ getLocalPort()459 protected int getLocalPort() 460 { 461 if (channel == null) 462 return -1; 463 try 464 { 465 InetSocketAddress local = channel.getVMChannel().getLocalAddress(); 466 if (local == null) 467 return -1; 468 return local.getPort(); 469 } 470 catch (IOException ioe) 471 { 472 return -1; 473 } 474 } 475 getLocalAddress()476 public InetSocketAddress getLocalAddress() 477 { 478 if (channel == null) 479 return null; 480 try 481 { 482 return channel.getVMChannel().getLocalAddress(); 483 } 484 catch (IOException ioe) 485 { 486 return null; 487 } 488 } 489 490 /* (non-Javadoc) 491 * @see java.net.SocketImpl#getPort() 492 */ getPort()493 protected int getPort() 494 { 495 if (channel == null) 496 return -1; 497 498 try 499 { 500 InetSocketAddress remote = channel.getVMChannel().getPeerAddress(); 501 if (remote == null) 502 return -1; 503 return remote.getPort(); 504 } 505 catch (IOException ioe) 506 { 507 return -1; 508 } 509 } 510 511 /** 512 * This class contains an implementation of <code>InputStream</code> for 513 * sockets. It in an internal only class used by <code>PlainSocketImpl</code>. 514 * 515 * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> 516 */ 517 final class SocketInputStream 518 extends InputStream 519 { 520 /** 521 * Returns the number of bytes available to be read before blocking 522 */ available()523 public int available() throws IOException 524 { 525 return PlainSocketImpl.this.available(); 526 } 527 528 /** 529 * This method not only closes the stream, it closes the underlying socket 530 * (and thus any connection) and invalidates any other Input/Output streams 531 * for the underlying impl object 532 */ close()533 public void close() throws IOException 534 { 535 PlainSocketImpl.this.close(); 536 } 537 538 /** 539 * Reads the next byte of data and returns it as an int. 540 * 541 * @return The byte read (as an int) or -1 if end of stream); 542 * 543 * @exception IOException If an error occurs. 544 */ read()545 public int read() throws IOException 546 { 547 if (channel == null) 548 throw new SocketException("not connected"); 549 while (true) 550 { 551 try 552 { 553 return channel.getVMChannel().read(); 554 } 555 catch (SocketTimeoutException ste) 556 { 557 throw ste; 558 } 559 catch (InterruptedIOException iioe) 560 { 561 // Ignore; NIO may throw this; net io shouldn't 562 } 563 } 564 } 565 566 /** 567 * Reads up to len bytes of data into the caller supplied buffer starting 568 * at offset bytes from the start of the buffer 569 * 570 * @param buf The buffer 571 * @param offset Offset into the buffer to start reading from 572 * @param len The number of bytes to read 573 * 574 * @return The number of bytes actually read or -1 if end of stream 575 * 576 * @exception IOException If an error occurs. 577 */ read(byte[] buf, int offset, int len)578 public int read (byte[] buf, int offset, int len) throws IOException 579 { 580 if (channel == null) 581 throw new SocketException("not connected"); 582 ByteBuffer b = ByteBuffer.wrap(buf, offset, len); 583 while (true) 584 { 585 try 586 { 587 return channel.read(b); 588 } 589 catch (SocketTimeoutException ste) 590 { 591 throw ste; 592 } 593 catch (InterruptedIOException iioe) 594 { 595 // Ignored; NIO may throw this; net IO not. 596 } 597 } 598 } 599 } 600 601 /** 602 * This class is used internally by <code>PlainSocketImpl</code> to be the 603 * <code>OutputStream</code> subclass returned by its 604 * <code>getOutputStream method</code>. It expects only to be used in that 605 * context. 606 * 607 * @author Nic Ferrier <nferrier@tapsellferrier.co.uk> 608 */ 609 final class SocketOutputStream 610 extends OutputStream 611 { 612 /** 613 * This method closes the stream and the underlying socket connection. This 614 * action also effectively closes any other InputStream or OutputStream 615 * object associated with the connection. 616 * 617 * @exception IOException If an error occurs 618 */ close()619 public void close() throws IOException 620 { 621 PlainSocketImpl.this.close(); 622 } 623 624 /** 625 * Writes a byte (passed in as an int) to the given output stream 626 * 627 * @param b The byte to write 628 * 629 * @exception IOException If an error occurs 630 */ write(int b)631 public void write(int b) throws IOException 632 { 633 if (channel == null) 634 throw new SocketException("not connected"); 635 while (true) 636 { 637 try 638 { 639 channel.getVMChannel().write(b); 640 return; 641 } 642 catch (InterruptedIOException iioe) 643 { 644 // Ignored. 645 } 646 } 647 } 648 649 /** 650 * Writes len number of bytes from the array buf to the stream starting 651 * at offset bytes into the buffer. 652 * 653 * @param buf The buffer 654 * @param offset Offset into the buffer to start writing from 655 * @param len The number of bytes to write 656 * 657 * @exception IOException If an error occurs. 658 */ write(byte[] buf, int offset, int len)659 public void write (byte[] buf, int offset, int len) throws IOException 660 { 661 if (channel == null) 662 throw new SocketException("not connected"); 663 ByteBuffer b = ByteBuffer.wrap(buf, offset, len); 664 while (b.hasRemaining()) 665 { 666 try 667 { 668 if (channel.write(b) == -1) 669 throw new IOException("channel has been closed"); 670 } 671 catch (InterruptedIOException iioe) 672 { 673 // Ignored. 674 } 675 } 676 } 677 } 678 } 679