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