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