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