001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net; 019 020import java.io.Closeable; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.net.InetAddress; 025import java.net.InetSocketAddress; 026import java.net.Socket; 027import java.net.SocketException; 028 029import javax.net.ServerSocketFactory; 030import javax.net.SocketFactory; 031 032 033/** 034 * The SocketClient provides the basic operations that are required of 035 * client objects accessing sockets. It is meant to be 036 * subclassed to avoid having to rewrite the same code over and over again 037 * to open a socket, close a socket, set timeouts, etc. Of special note 038 * is the {@link #setSocketFactory setSocketFactory } 039 * method, which allows you to control the type of Socket the SocketClient 040 * creates for initiating network connections. This is especially useful 041 * for adding SSL or proxy support as well as better support for applets. For 042 * example, you could create a 043 * {@link javax.net.SocketFactory} that 044 * requests browser security capabilities before creating a socket. 045 * All classes derived from SocketClient should use the 046 * {@link #_socketFactory_ _socketFactory_ } member variable to 047 * create Socket and ServerSocket instances rather than instantiating 048 * them by directly invoking a constructor. By honoring this contract 049 * you guarantee that a user will always be able to provide his own 050 * Socket implementations by substituting his own SocketFactory. 051 * @see SocketFactory 052 */ 053public abstract class SocketClient 054{ 055 /** 056 * The end of line character sequence used by most IETF protocols. That 057 * is a carriage return followed by a newline: "\r\n" 058 */ 059 public static final String NETASCII_EOL = "\r\n"; 060 061 /** The default SocketFactory shared by all SocketClient instances. */ 062 private static final SocketFactory __DEFAULT_SOCKET_FACTORY = 063 SocketFactory.getDefault(); 064 065 /** The default {@link ServerSocketFactory} */ 066 private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = 067 ServerSocketFactory.getDefault(); 068 069 /** 070 * A ProtocolCommandSupport object used to manage the registering of 071 * ProtocolCommandListeners and te firing of ProtocolCommandEvents. 072 */ 073 private ProtocolCommandSupport __commandSupport; 074 075 /** The timeout to use after opening a socket. */ 076 protected int _timeout_; 077 078 /** The socket used for the connection. */ 079 protected Socket _socket_; 080 081 /** The default port the client should connect to. */ 082 protected int _defaultPort_; 083 084 /** The socket's InputStream. */ 085 protected InputStream _input_; 086 087 /** The socket's OutputStream. */ 088 protected OutputStream _output_; 089 090 /** The socket's SocketFactory. */ 091 protected SocketFactory _socketFactory_; 092 093 /** The socket's ServerSocket Factory. */ 094 protected ServerSocketFactory _serverSocketFactory_; 095 096 /** The socket's connect timeout (0 = infinite timeout) */ 097 private static final int DEFAULT_CONNECT_TIMEOUT = 0; 098 protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; 099 100 /** Hint for SO_RCVBUF size */ 101 private int receiveBufferSize = -1; 102 103 /** Hint for SO_SNDBUF size */ 104 private int sendBufferSize = -1; 105 106 /** 107 * Default constructor for SocketClient. Initializes 108 * _socket_ to null, _timeout_ to 0, _defaultPort to 0, 109 * _isConnected_ to false, and _socketFactory_ to a shared instance of 110 * {@link org.apache.commons.net.DefaultSocketFactory}. 111 */ 112 public SocketClient() 113 { 114 _socket_ = null; 115 _input_ = null; 116 _output_ = null; 117 _timeout_ = 0; 118 _defaultPort_ = 0; 119 _socketFactory_ = __DEFAULT_SOCKET_FACTORY; 120 _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; 121 } 122 123 124 /** 125 * Because there are so many connect() methods, the _connectAction_() 126 * method is provided as a means of performing some action immediately 127 * after establishing a connection, rather than reimplementing all 128 * of the connect() methods. The last action performed by every 129 * connect() method after opening a socket is to call this method. 130 * <p> 131 * This method sets the timeout on the just opened socket to the default 132 * timeout set by {@link #setDefaultTimeout setDefaultTimeout() }, 133 * sets _input_ and _output_ to the socket's InputStream and OutputStream 134 * respectively, and sets _isConnected_ to true. 135 * <p> 136 * Subclasses overriding this method should start by calling 137 * <code> super._connectAction_() </code> first to ensure the 138 * initialization of the aforementioned protected variables. 139 */ 140 protected void _connectAction_() throws IOException 141 { 142 _socket_.setSoTimeout(_timeout_); 143 _input_ = _socket_.getInputStream(); 144 _output_ = _socket_.getOutputStream(); 145 } 146 147 148 /** 149 * Opens a Socket connected to a remote host at the specified port and 150 * originating from the current host at a system assigned port. 151 * Before returning, {@link #_connectAction_ _connectAction_() } 152 * is called to perform connection initialization actions. 153 * <p> 154 * @param host The remote host. 155 * @param port The port to connect to on the remote host. 156 * @exception SocketException If the socket timeout could not be set. 157 * @exception IOException If the socket could not be opened. In most 158 * cases you will only want to catch IOException since SocketException is 159 * derived from it. 160 */ 161 public void connect(InetAddress host, int port) 162 throws SocketException, IOException 163 { 164 _socket_ = _socketFactory_.createSocket(); 165 if (receiveBufferSize != -1) { 166 _socket_.setReceiveBufferSize(receiveBufferSize); 167 } 168 if (sendBufferSize != -1) { 169 _socket_.setSendBufferSize(sendBufferSize); 170 } 171 _socket_.connect(new InetSocketAddress(host, port), connectTimeout); 172 _connectAction_(); 173 } 174 175 /** 176 * Opens a Socket connected to a remote host at the specified port and 177 * originating from the current host at a system assigned port. 178 * Before returning, {@link #_connectAction_ _connectAction_() } 179 * is called to perform connection initialization actions. 180 * <p> 181 * @param hostname The name of the remote host. 182 * @param port The port to connect to on the remote host. 183 * @exception SocketException If the socket timeout could not be set. 184 * @exception IOException If the socket could not be opened. In most 185 * cases you will only want to catch IOException since SocketException is 186 * derived from it. 187 * @exception java.net.UnknownHostException If the hostname cannot be resolved. 188 */ 189 public void connect(String hostname, int port) 190 throws SocketException, IOException 191 { 192 connect(InetAddress.getByName(hostname), port); 193 } 194 195 196 /** 197 * Opens a Socket connected to a remote host at the specified port and 198 * originating from the specified local address and port. 199 * Before returning, {@link #_connectAction_ _connectAction_() } 200 * is called to perform connection initialization actions. 201 * <p> 202 * @param host The remote host. 203 * @param port The port to connect to on the remote host. 204 * @param localAddr The local address to use. 205 * @param localPort The local port to use. 206 * @exception SocketException If the socket timeout could not be set. 207 * @exception IOException If the socket could not be opened. In most 208 * cases you will only want to catch IOException since SocketException is 209 * derived from it. 210 */ 211 public void connect(InetAddress host, int port, 212 InetAddress localAddr, int localPort) 213 throws SocketException, IOException 214 { 215 _socket_ = _socketFactory_.createSocket(); 216 if (receiveBufferSize != -1) { 217 _socket_.setReceiveBufferSize(receiveBufferSize); 218 } 219 if (sendBufferSize != -1) { 220 _socket_.setSendBufferSize(sendBufferSize); 221 } 222 _socket_.bind(new InetSocketAddress(localAddr, localPort)); 223 _socket_.connect(new InetSocketAddress(host, port), connectTimeout); 224 _connectAction_(); 225 } 226 227 228 /** 229 * Opens a Socket connected to a remote host at the specified port and 230 * originating from the specified local address and port. 231 * Before returning, {@link #_connectAction_ _connectAction_() } 232 * is called to perform connection initialization actions. 233 * <p> 234 * @param hostname The name of the remote host. 235 * @param port The port to connect to on the remote host. 236 * @param localAddr The local address to use. 237 * @param localPort The local port to use. 238 * @exception SocketException If the socket timeout could not be set. 239 * @exception IOException If the socket could not be opened. In most 240 * cases you will only want to catch IOException since SocketException is 241 * derived from it. 242 * @exception java.net.UnknownHostException If the hostname cannot be resolved. 243 */ 244 public void connect(String hostname, int port, 245 InetAddress localAddr, int localPort) 246 throws SocketException, IOException 247 { 248 connect(InetAddress.getByName(hostname), port, localAddr, localPort); 249 } 250 251 252 /** 253 * Opens a Socket connected to a remote host at the current default port 254 * and originating from the current host at a system assigned port. 255 * Before returning, {@link #_connectAction_ _connectAction_() } 256 * is called to perform connection initialization actions. 257 * <p> 258 * @param host The remote host. 259 * @exception SocketException If the socket timeout could not be set. 260 * @exception IOException If the socket could not be opened. In most 261 * cases you will only want to catch IOException since SocketException is 262 * derived from it. 263 */ 264 public void connect(InetAddress host) throws SocketException, IOException 265 { 266 connect(host, _defaultPort_); 267 } 268 269 270 /** 271 * Opens a Socket connected to a remote host at the current default 272 * port and originating from the current host at a system assigned port. 273 * Before returning, {@link #_connectAction_ _connectAction_() } 274 * is called to perform connection initialization actions. 275 * <p> 276 * @param hostname The name of the remote host. 277 * @exception SocketException If the socket timeout could not be set. 278 * @exception IOException If the socket could not be opened. In most 279 * cases you will only want to catch IOException since SocketException is 280 * derived from it. 281 * @exception java.net.UnknownHostException If the hostname cannot be resolved. 282 */ 283 public void connect(String hostname) throws SocketException, IOException 284 { 285 connect(hostname, _defaultPort_); 286 } 287 288 289 /** 290 * Disconnects the socket connection. 291 * You should call this method after you've finished using the class 292 * instance and also before you call 293 * {@link #connect connect() } 294 * again. _isConnected_ is set to false, _socket_ is set to null, 295 * _input_ is set to null, and _output_ is set to null. 296 * <p> 297 * @exception IOException If there is an error closing the socket. 298 */ 299 public void disconnect() throws IOException 300 { 301 closeQuietly(_socket_); 302 closeQuietly(_input_); 303 closeQuietly(_output_); 304 _socket_ = null; 305 _input_ = null; 306 _output_ = null; 307 } 308 309 private void closeQuietly(Socket socket) { 310 if (socket != null){ 311 try { 312 socket.close(); 313 } catch (IOException e) { 314 } 315 } 316 } 317 318 private void closeQuietly(Closeable close){ 319 if (close != null){ 320 try { 321 close.close(); 322 } catch (IOException e) { 323 } 324 } 325 } 326 /** 327 * Returns true if the client is currently connected to a server. 328 * <p> 329 * Delegates to {@link Socket#isConnected()} 330 * @return True if the client is currently connected to a server, 331 * false otherwise. 332 */ 333 public boolean isConnected() 334 { 335 if (_socket_ == null) { 336 return false; 337 } 338 339 return _socket_.isConnected(); 340 } 341 342 /** 343 * Make various checks on the socket to test if it is available for use. 344 * Note that the only sure test is to use it, but these checks may help 345 * in some cases. 346 * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a> 347 * @return {@code true} if the socket appears to be available for use 348 * @since 3.0 349 */ 350 public boolean isAvailable(){ 351 if (isConnected()) { 352 try 353 { 354 if (_socket_.getInetAddress() == null) { 355 return false; 356 } 357 if (_socket_.getPort() == 0) { 358 return false; 359 } 360 if (_socket_.getRemoteSocketAddress() == null) { 361 return false; 362 } 363 if (_socket_.isClosed()) { 364 return false; 365 } 366 /* these aren't exact checks (a Socket can be half-open), 367 but since we usually require two-way data transfer, 368 we check these here too: */ 369 if (_socket_.isInputShutdown()) { 370 return false; 371 } 372 if (_socket_.isOutputShutdown()) { 373 return false; 374 } 375 /* ignore the result, catch exceptions: */ 376 _socket_.getInputStream(); 377 _socket_.getOutputStream(); 378 } 379 catch (IOException ioex) 380 { 381 return false; 382 } 383 return true; 384 } else { 385 return false; 386 } 387 } 388 389 /** 390 * Sets the default port the SocketClient should connect to when a port 391 * is not specified. The {@link #_defaultPort_ _defaultPort_ } 392 * variable stores this value. If never set, the default port is equal 393 * to zero. 394 * <p> 395 * @param port The default port to set. 396 */ 397 public void setDefaultPort(int port) 398 { 399 _defaultPort_ = port; 400 } 401 402 /** 403 * Returns the current value of the default port (stored in 404 * {@link #_defaultPort_ _defaultPort_ }). 405 * <p> 406 * @return The current value of the default port. 407 */ 408 public int getDefaultPort() 409 { 410 return _defaultPort_; 411 } 412 413 414 /** 415 * Set the default timeout in milliseconds to use when opening a socket. 416 * This value is only used previous to a call to 417 * {@link #connect connect()} 418 * and should not be confused with {@link #setSoTimeout setSoTimeout()} 419 * which operates on an the currently opened socket. _timeout_ contains 420 * the new timeout value. 421 * <p> 422 * @param timeout The timeout in milliseconds to use for the socket 423 * connection. 424 */ 425 public void setDefaultTimeout(int timeout) 426 { 427 _timeout_ = timeout; 428 } 429 430 431 /** 432 * Returns the default timeout in milliseconds that is used when 433 * opening a socket. 434 * <p> 435 * @return The default timeout in milliseconds that is used when 436 * opening a socket. 437 */ 438 public int getDefaultTimeout() 439 { 440 return _timeout_; 441 } 442 443 444 /** 445 * Set the timeout in milliseconds of a currently open connection. 446 * Only call this method after a connection has been opened 447 * by {@link #connect connect()}. 448 * <p> 449 * @param timeout The timeout in milliseconds to use for the currently 450 * open socket connection. 451 * @exception SocketException If the operation fails. 452 */ 453 public void setSoTimeout(int timeout) throws SocketException 454 { 455 _socket_.setSoTimeout(timeout); 456 } 457 458 459 /** 460 * Set the underlying socket send buffer size. 461 * <p> 462 * @param size The size of the buffer in bytes. 463 * @throws SocketException 464 * @since 2.0 465 */ 466 public void setSendBufferSize(int size) throws SocketException { 467 sendBufferSize = size; 468 } 469 470 /** 471 * Get the current sendBuffer size 472 * @return the size, or -1 if not initialised 473 * @since 3.0 474 */ 475 protected int getSendBufferSize(){ 476 return sendBufferSize; 477 } 478 479 /** 480 * Sets the underlying socket receive buffer size. 481 * <p> 482 * @param size The size of the buffer in bytes. 483 * @throws SocketException 484 * @since 2.0 485 */ 486 public void setReceiveBufferSize(int size) throws SocketException { 487 receiveBufferSize = size; 488 } 489 490 /** 491 * Get the current receivedBuffer size 492 * @return the size, or -1 if not initialised 493 * @since 3.0 494 */ 495 protected int getReceiveBufferSize(){ 496 return receiveBufferSize; 497 } 498 499 /** 500 * Returns the timeout in milliseconds of the currently opened socket. 501 * <p> 502 * @return The timeout in milliseconds of the currently opened socket. 503 * @exception SocketException If the operation fails. 504 */ 505 public int getSoTimeout() throws SocketException 506 { 507 return _socket_.getSoTimeout(); 508 } 509 510 /** 511 * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the 512 * currently opened socket. 513 * <p> 514 * @param on True if Nagle's algorithm is to be enabled, false if not. 515 * @exception SocketException If the operation fails. 516 */ 517 public void setTcpNoDelay(boolean on) throws SocketException 518 { 519 _socket_.setTcpNoDelay(on); 520 } 521 522 523 /** 524 * Returns true if Nagle's algorithm is enabled on the currently opened 525 * socket. 526 * <p> 527 * @return True if Nagle's algorithm is enabled on the currently opened 528 * socket, false otherwise. 529 * @exception SocketException If the operation fails. 530 */ 531 public boolean getTcpNoDelay() throws SocketException 532 { 533 return _socket_.getTcpNoDelay(); 534 } 535 536 /** 537 * Sets the SO_KEEPALIVE flag on the currently opened socket. 538 * 539 * From the Javadocs, the default keepalive time is 2 hours (although this is 540 * implementation dependent). It looks as though the Windows WSA sockets implementation 541 * allows a specific keepalive value to be set, although this seems not to be the case on 542 * other systems. 543 * @param keepAlive If true, keepAlive is turned on 544 * @throws SocketException 545 * @since 2.2 546 */ 547 public void setKeepAlive(boolean keepAlive) throws SocketException { 548 _socket_.setKeepAlive(keepAlive); 549 } 550 551 /** 552 * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. 553 * Delegates to {@link Socket#getKeepAlive()} 554 * @return True if SO_KEEPALIVE is enabled. 555 * @throws SocketException 556 * @since 2.2 557 */ 558 public boolean getKeepAlive() throws SocketException { 559 return _socket_.getKeepAlive(); 560 } 561 562 /** 563 * Sets the SO_LINGER timeout on the currently opened socket. 564 * <p> 565 * @param on True if linger is to be enabled, false if not. 566 * @param val The linger timeout (in hundredths of a second?) 567 * @exception SocketException If the operation fails. 568 */ 569 public void setSoLinger(boolean on, int val) throws SocketException 570 { 571 _socket_.setSoLinger(on, val); 572 } 573 574 575 /** 576 * Returns the current SO_LINGER timeout of the currently opened socket. 577 * <p> 578 * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns 579 * -1. 580 * @exception SocketException If the operation fails. 581 */ 582 public int getSoLinger() throws SocketException 583 { 584 return _socket_.getSoLinger(); 585 } 586 587 588 /** 589 * Returns the port number of the open socket on the local host used 590 * for the connection. 591 * Delegates to {@link Socket#getLocalPort()} 592 * <p> 593 * @return The port number of the open socket on the local host used 594 * for the connection. 595 */ 596 public int getLocalPort() 597 { 598 return _socket_.getLocalPort(); 599 } 600 601 602 /** 603 * Returns the local address to which the client's socket is bound. 604 * Delegates to {@link Socket#getLocalAddress()} 605 * <p> 606 * @return The local address to which the client's socket is bound. 607 */ 608 public InetAddress getLocalAddress() 609 { 610 return _socket_.getLocalAddress(); 611 } 612 613 /** 614 * Returns the port number of the remote host to which the client is 615 * connected. 616 * Delegates to {@link Socket#getPort()} 617 * <p> 618 * @return The port number of the remote host to which the client is 619 * connected. 620 */ 621 public int getRemotePort() 622 { 623 return _socket_.getPort(); 624 } 625 626 627 /** 628 * @return The remote address to which the client is connected. 629 * Delegates to {@link Socket#getInetAddress()} 630 */ 631 public InetAddress getRemoteAddress() 632 { 633 return _socket_.getInetAddress(); 634 } 635 636 637 /** 638 * Verifies that the remote end of the given socket is connected to the 639 * the same host that the SocketClient is currently connected to. This 640 * is useful for doing a quick security check when a client needs to 641 * accept a connection from a server, such as an FTP data connection or 642 * a BSD R command standard error stream. 643 * <p> 644 * @return True if the remote hosts are the same, false if not. 645 */ 646 public boolean verifyRemote(Socket socket) 647 { 648 InetAddress host1, host2; 649 650 host1 = socket.getInetAddress(); 651 host2 = getRemoteAddress(); 652 653 return host1.equals(host2); 654 } 655 656 657 /** 658 * Sets the SocketFactory used by the SocketClient to open socket 659 * connections. If the factory value is null, then a default 660 * factory is used (only do this to reset the factory after having 661 * previously altered it). 662 * <p> 663 * @param factory The new SocketFactory the SocketClient should use. 664 */ 665 public void setSocketFactory(SocketFactory factory) 666 { 667 if (factory == null) { 668 _socketFactory_ = __DEFAULT_SOCKET_FACTORY; 669 } else { 670 _socketFactory_ = factory; 671 } 672 } 673 674 /** 675 * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket 676 * connections. If the factory value is null, then a default 677 * factory is used (only do this to reset the factory after having 678 * previously altered it). 679 * <p> 680 * @param factory The new ServerSocketFactory the SocketClient should use. 681 * @since 2.0 682 */ 683 public void setServerSocketFactory(ServerSocketFactory factory) { 684 if (factory == null) { 685 _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; 686 } else { 687 _serverSocketFactory_ = factory; 688 } 689 } 690 691 /** 692 * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's 693 * connect() method. 694 * @param connectTimeout The connection timeout to use (in ms) 695 * @since 2.0 696 */ 697 public void setConnectTimeout(int connectTimeout) { 698 this.connectTimeout = connectTimeout; 699 } 700 701 /** 702 * Get the underlying socket connection timeout. 703 * @return timeout (in ms) 704 * @since 2.0 705 */ 706 public int getConnectTimeout() { 707 return connectTimeout; 708 } 709 710 /** 711 * Get the underlying {@link ServerSocketFactory} 712 * @return The server socket factory 713 * @since 2.2 714 */ 715 public ServerSocketFactory getServerSocketFactory() { 716 return _serverSocketFactory_; 717 } 718 719 720 /** 721 * Adds a ProtocolCommandListener. 722 * 723 * @param listener The ProtocolCommandListener to add. 724 * @since 3.0 725 */ 726 public void addProtocolCommandListener(ProtocolCommandListener listener) { 727 getCommandSupport().addProtocolCommandListener(listener); 728 } 729 730 /** 731 * Removes a ProtocolCommandListener. 732 * 733 * @param listener The ProtocolCommandListener to remove. 734 * @since 3.0 735 */ 736 public void removeProtocolCommandListener(ProtocolCommandListener listener) { 737 getCommandSupport().removeProtocolCommandListener(listener); 738 } 739 740 /** 741 * If there are any listeners, send them the reply details. 742 * 743 * @param replyCode the code extracted from the reply 744 * @param reply the full reply text 745 * @since 3.0 746 */ 747 protected void fireReplyReceived(int replyCode, String reply) { 748 if (getCommandSupport().getListenerCount() > 0) { 749 getCommandSupport().fireReplyReceived(replyCode, reply); 750 } 751 } 752 753 /** 754 * If there are any listeners, send them the command details. 755 * 756 * @param command the command name 757 * @param message the complete message, including command name 758 * @since 3.0 759 */ 760 protected void fireCommandSent(String command, String message) { 761 if (getCommandSupport().getListenerCount() > 0) { 762 getCommandSupport().fireCommandSent(command, message); 763 } 764 } 765 766 /** 767 * Create the CommandSupport instance if required 768 */ 769 protected void createCommandSupport(){ 770 __commandSupport = new ProtocolCommandSupport(this); 771 } 772 773 /** 774 * Subclasses can override this if they need to provide their own 775 * instance field for backwards compatibilty. 776 * 777 * @return the CommandSupport instance, may be {@code null} 778 * @since 3.0 779 */ 780 protected ProtocolCommandSupport getCommandSupport() { 781 return __commandSupport; 782 } 783 784 /* 785 * N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility, 786 * so the abstract method is needed to pass the instance to the methods which were moved here. 787 */ 788} 789 790