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