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.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.Socket;
26  import java.net.SocketException;
27  import java.net.UnknownHostException;
28  
29  import javax.net.ServerSocketFactory;
30  import javax.net.SocketFactory;
31  
32  
33  /**
34   * The SocketClient provides the basic operations that are required of
35   * client objects accessing sockets.  It is meant to be
36   * subclassed to avoid having to rewrite the same code over and over again
37   * to open a socket, close a socket, set timeouts, etc.  Of special note
38   * is the {@link #setSocketFactory  setSocketFactory }
39   * method, which allows you to control the type of Socket the SocketClient
40   * creates for initiating network connections.  This is especially useful
41   * for adding SSL or proxy support as well as better support for applets.  For
42   * example, you could create a
43   * {@link org.apache.commons.net.SocketFactory} that
44   * requests browser security capabilities before creating a socket.
45   * All classes derived from SocketClient should use the
46   * {@link #_socketFactory_  _socketFactory_ } member variable to
47   * create Socket and ServerSocket instances rather than instanting
48   * them by directly invoking a constructor.  By honoring this contract
49   * you guarantee that a user will always be able to provide his own
50   * Socket implementations by substituting his own SocketFactory.
51   * @author Daniel F. Savarese
52   * @see SocketFactory
53   */
54  public abstract class SocketClient
55  {
56      /**
57       * The end of line character sequence used by most IETF protocols.  That
58       * is a carriage return followed by a newline: "\r\n"
59       */
60      public static final String NETASCII_EOL = "\r\n";
61  
62      /** The default SocketFactory shared by all SocketClient instances. */
63      private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
64              SocketFactory.getDefault();
65      
66      private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = 
67              ServerSocketFactory.getDefault();
68  
69      /** The timeout to use after opening a socket. */
70      protected int _timeout_;
71  
72      /** The socket used for the connection. */
73      protected Socket _socket_;
74  
75      /** The default port the client should connect to. */
76      protected int _defaultPort_;
77  
78      /** The socket's InputStream. */
79      protected InputStream _input_;
80  
81      /** The socket's OutputStream. */
82      protected OutputStream _output_;
83  
84      /** The socket's SocketFactory. */
85      protected SocketFactory _socketFactory_;
86      
87      /** The socket's ServerSocket Factory. */
88      protected ServerSocketFactory _serverSocketFactory_;
89      
90      /** The socket's connect timeout (0 = infinite timeout) */
91      private static final int DEFAULT_CONNECT_TIMEOUT = 0;
92      protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
93  
94      /**
95       * Default constructor for SocketClient.  Initializes
96       * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
97       * _isConnected_ to false, and _socketFactory_ to a shared instance of
98       * {@link org.apache.commons.net.DefaultSocketFactory}.
99       */
100     public SocketClient()
101     {
102         _socket_ = null;
103         _input_ = null;
104         _output_ = null;
105         _timeout_ = 0;
106         _defaultPort_ = 0;
107         _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
108         _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
109     }
110 
111 
112     /**
113      * Because there are so many connect() methods, the _connectAction_()
114      * method is provided as a means of performing some action immediately
115      * after establishing a connection, rather than reimplementing all
116      * of the connect() methods.  The last action performed by every
117      * connect() method after opening a socket is to call this method.
118      * <p>
119      * This method sets the timeout on the just opened socket to the default
120      * timeout set by {@link #setDefaultTimeout  setDefaultTimeout() },
121      * sets _input_ and _output_ to the socket's InputStream and OutputStream
122      * respectively, and sets _isConnected_ to true.
123      * <p>
124      * Subclasses overriding this method should start by calling
125      * <code> super._connectAction_() </code> first to ensure the
126      * initialization of the aforementioned protected variables.
127      */
128     protected void _connectAction_() throws IOException
129     {
130         _socket_.setSoTimeout(_timeout_);
131         _input_ = _socket_.getInputStream();
132         _output_ = _socket_.getOutputStream();
133     }
134 
135 
136     /**
137      * Opens a Socket connected to a remote host at the specified port and
138      * originating from the current host at a system assigned port.
139      * Before returning, {@link #_connectAction_  _connectAction_() }
140      * is called to perform connection initialization actions.
141      * <p>
142      * @param host  The remote host.
143      * @param port  The port to connect to on the remote host.
144      * @exception SocketException If the socket timeout could not be set.
145      * @exception IOException If the socket could not be opened.  In most
146      *  cases you will only want to catch IOException since SocketException is
147      *  derived from it.
148      */
149     public void connect(InetAddress host, int port)
150     throws SocketException, IOException
151     {
152         _socket_ = _socketFactory_.createSocket();
153         _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
154 
155         _connectAction_();
156     }
157 
158     /**
159      * Opens a Socket connected to a remote host at the specified port and
160      * originating from the current host at a system assigned port.
161      * Before returning, {@link #_connectAction_  _connectAction_() }
162      * is called to perform connection initialization actions.
163      * <p>
164      * @param hostname  The name of the remote host.
165      * @param port  The port to connect to on the remote host.
166      * @exception SocketException If the socket timeout could not be set.
167      * @exception IOException If the socket could not be opened.  In most
168      *  cases you will only want to catch IOException since SocketException is
169      *  derived from it.
170      * @exception UnknownHostException If the hostname cannot be resolved.
171      */
172     public void connect(String hostname, int port)
173     throws SocketException, IOException
174     {
175         _socket_= _socketFactory_.createSocket();
176         _socket_.connect(new InetSocketAddress(hostname, port), connectTimeout);
177         
178         _connectAction_();
179     }
180 
181 
182     /**
183      * Opens a Socket connected to a remote host at the specified port and
184      * originating from the specified local address and port.
185      * Before returning, {@link #_connectAction_  _connectAction_() }
186      * is called to perform connection initialization actions.
187      * <p>
188      * @param host  The remote host.
189      * @param port  The port to connect to on the remote host.
190      * @param localAddr  The local address to use.
191      * @param localPort  The local port to use.
192      * @exception SocketException If the socket timeout could not be set.
193      * @exception 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      */
197     public void connect(InetAddress host, int port,
198                         InetAddress localAddr, int localPort)
199     throws SocketException, IOException
200     {
201         _socket_ = _socketFactory_.createSocket();
202         _socket_.bind(new InetSocketAddress(localAddr, localPort));
203         _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
204         
205         _connectAction_();
206     }
207 
208 
209     /**
210      * Opens a Socket connected to a remote host at the specified port and
211      * originating from the specified local address and port.
212      * Before returning, {@link #_connectAction_  _connectAction_() }
213      * is called to perform connection initialization actions.
214      * <p>
215      * @param hostname  The name of the remote host.
216      * @param port  The port to connect to on the remote host.
217      * @param localAddr  The local address to use.
218      * @param localPort  The local port to use.
219      * @exception SocketException If the socket timeout could not be set.
220      * @exception IOException If the socket could not be opened.  In most
221      *  cases you will only want to catch IOException since SocketException is
222      *  derived from it.
223      * @exception UnknownHostException If the hostname cannot be resolved.
224      */
225     public void connect(String hostname, int port,
226                         InetAddress localAddr, int localPort)
227     throws SocketException, IOException
228     {
229         _socket_ =
230             _socketFactory_.createSocket(hostname, port, localAddr, localPort);
231         _connectAction_();
232     }
233 
234 
235     /**
236      * Opens a Socket connected to a remote host at the current default port
237      * and originating from the current host at a system assigned port.
238      * Before returning, {@link #_connectAction_  _connectAction_() }
239      * is called to perform connection initialization actions.
240      * <p>
241      * @param host  The remote host.
242      * @exception SocketException If the socket timeout could not be set.
243      * @exception IOException If the socket could not be opened.  In most
244      *  cases you will only want to catch IOException since SocketException is
245      *  derived from it.
246      */
247     public void connect(InetAddress host) throws SocketException, IOException
248     {
249         connect(host, _defaultPort_);
250     }
251 
252 
253     /**
254      * Opens a Socket connected to a remote host at the current default
255      * port and originating from the current host at a system assigned port.
256      * Before returning, {@link #_connectAction_  _connectAction_() }
257      * is called to perform connection initialization actions.
258      * <p>
259      * @param hostname  The name of the remote host.
260      * @exception SocketException If the socket timeout could not be set.
261      * @exception IOException If the socket could not be opened.  In most
262      *  cases you will only want to catch IOException since SocketException is
263      *  derived from it.
264      * @exception UnknownHostException If the hostname cannot be resolved.
265      */
266     public void connect(String hostname) throws SocketException, IOException
267     {
268         connect(hostname, _defaultPort_);
269     }
270 
271 
272     /**
273      * Disconnects the socket connection.
274      * You should call this method after you've finished using the class
275      * instance and also before you call
276      * {@link #connect connect() }
277      * again.  _isConnected_ is set to false, _socket_ is set to null,
278      * _input_ is set to null, and _output_ is set to null.
279      * <p>
280      * @exception IOException  If there is an error closing the socket.
281      */
282     public void disconnect() throws IOException
283     {
284         if (_socket_ != null) _socket_.close();
285         if (_input_ != null) _input_.close();
286         if (_output_ != null) _output_.close();
287         if (_socket_ != null) _socket_ = null;
288         _input_ = null;
289         _output_ = null;
290     }
291 
292 
293     /**
294      * Returns true if the client is currently connected to a server.
295      * <p>
296      * @return True if the client is currently connected to a server,
297      *         false otherwise.
298      */
299     public boolean isConnected()
300     {
301         if (_socket_ == null)
302             return false;
303         
304         return _socket_.isConnected();
305     }
306 
307 
308     /**
309      * Sets the default port the SocketClient should connect to when a port
310      * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
311      * variable stores this value.  If never set, the default port is equal
312      * to zero.
313      * <p>
314      * @param port  The default port to set.
315      */
316     public void setDefaultPort(int port)
317     {
318         _defaultPort_ = port;
319     }
320 
321     /**
322      * Returns the current value of the default port (stored in
323      * {@link #_defaultPort_  _defaultPort_ }).
324      * <p>
325      * @return The current value of the default port.
326      */
327     public int getDefaultPort()
328     {
329         return _defaultPort_;
330     }
331 
332 
333     /**
334      * Set the default timeout in milliseconds to use when opening a socket.
335      * This value is only used previous to a call to
336      * {@link #connect connect()}
337      * and should not be confused with {@link #setSoTimeout setSoTimeout()}
338      * which operates on an the currently opened socket.  _timeout_ contains
339      * the new timeout value.
340      * <p>
341      * @param timeout  The timeout in milliseconds to use for the socket
342      *                 connection.
343      */
344     public void setDefaultTimeout(int timeout)
345     {
346         _timeout_ = timeout;
347     }
348 
349 
350     /**
351      * Returns the default timeout in milliseconds that is used when
352      * opening a socket.
353      * <p>
354      * @return The default timeout in milliseconds that is used when
355      *         opening a socket.
356      */
357     public int getDefaultTimeout()
358     {
359         return _timeout_;
360     }
361 
362 
363     /**
364      * Set the timeout in milliseconds of a currently open connection.
365      * Only call this method after a connection has been opened
366      * by {@link #connect connect()}.
367      * <p>
368      * @param timeout  The timeout in milliseconds to use for the currently
369      *                 open socket connection.
370      * @exception SocketException If the operation fails.
371      */
372     public void setSoTimeout(int timeout) throws SocketException
373     {
374         _socket_.setSoTimeout(timeout);
375     }
376     
377     
378     /**
379      * Set the underlying socket send buffer size.
380      * <p>
381      * @param size The size of the buffer in bytes.
382      * @throws SocketException 
383      * @since 2.0
384      */
385     public void setSendBufferSize(int size) throws SocketException {
386         _socket_.setSendBufferSize(size);
387     }
388     
389     
390     /**
391      * Sets the underlying socket receive buffer size.
392      * <p>
393      * @param size The size of the buffer in bytes.
394      * @throws SocketException 
395      * @since 2.0
396      */
397     public void setReceiveBufferSize(int size) throws SocketException  {
398         _socket_.setReceiveBufferSize(size);
399     }
400 
401 
402     /**
403      * Returns the timeout in milliseconds of the currently opened socket.
404      * <p>
405      * @return The timeout in milliseconds of the currently opened socket.
406      * @exception SocketException If the operation fails.
407      */
408     public int getSoTimeout() throws SocketException
409     {
410         return _socket_.getSoTimeout();
411     }
412 
413     /**
414      * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
415      * currently opened socket.
416      * <p>
417      * @param on  True if Nagle's algorithm is to be enabled, false if not.
418      * @exception SocketException If the operation fails.
419      */
420     public void setTcpNoDelay(boolean on) throws SocketException
421     {
422         _socket_.setTcpNoDelay(on);
423     }
424 
425 
426     /**
427      * Returns true if Nagle's algorithm is enabled on the currently opened
428      * socket.
429      * <p>
430      * @return True if Nagle's algorithm is enabled on the currently opened
431      *        socket, false otherwise.
432      * @exception SocketException If the operation fails.
433      */
434     public boolean getTcpNoDelay() throws SocketException
435     {
436         return _socket_.getTcpNoDelay();
437     }
438 
439 
440     /**
441      * Sets the SO_LINGER timeout on the currently opened socket.
442      * <p>
443      * @param on  True if linger is to be enabled, false if not.
444      * @param val The linger timeout (in hundredths of a second?)
445      * @exception SocketException If the operation fails.
446      */
447     public void setSoLinger(boolean on, int val) throws SocketException
448     {
449         _socket_.setSoLinger(on, val);
450     }
451 
452 
453     /**
454      * Returns the current SO_LINGER timeout of the currently opened socket.
455      * <p>
456      * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
457      *         -1.
458      * @exception SocketException If the operation fails.
459      */
460     public int getSoLinger() throws SocketException
461     {
462         return _socket_.getSoLinger();
463     }
464 
465 
466     /**
467      * Returns the port number of the open socket on the local host used
468      * for the connection.
469      * <p>
470      * @return The port number of the open socket on the local host used
471      *         for the connection.
472      */
473     public int getLocalPort()
474     {
475         return _socket_.getLocalPort();
476     }
477 
478 
479     /**
480      * Returns the local address to which the client's socket is bound.
481      * <p>
482      * @return The local address to which the client's socket is bound.
483      */
484     public InetAddress getLocalAddress()
485     {
486         return _socket_.getLocalAddress();
487     }
488 
489     /**
490      * Returns the port number of the remote host to which the client is
491      * connected.
492      * <p>
493      * @return The port number of the remote host to which the client is
494      *         connected.
495      */
496     public int getRemotePort()
497     {
498         return _socket_.getPort();
499     }
500 
501 
502     /**
503      * @return The remote address to which the client is connected.
504      */
505     public InetAddress getRemoteAddress()
506     {
507         return _socket_.getInetAddress();
508     }
509 
510 
511     /**
512      * Verifies that the remote end of the given socket is connected to the
513      * the same host that the SocketClient is currently connected to.  This
514      * is useful for doing a quick security check when a client needs to
515      * accept a connection from a server, such as an FTP data connection or
516      * a BSD R command standard error stream.
517      * <p>
518      * @return True if the remote hosts are the same, false if not.
519      */
520     public boolean verifyRemote(Socket socket)
521     {
522         InetAddress host1, host2;
523 
524         host1 = socket.getInetAddress();
525         host2 = getRemoteAddress();
526 
527         return host1.equals(host2);
528     }
529 
530 
531     /**
532      * Sets the SocketFactory used by the SocketClient to open socket
533      * connections.  If the factory value is null, then a default
534      * factory is used (only do this to reset the factory after having
535      * previously altered it).
536      * <p>
537      * @param factory  The new SocketFactory the SocketClient should use.
538      */
539     public void setSocketFactory(SocketFactory factory)
540     {
541         if (factory == null)
542             _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
543         else
544             _socketFactory_ = factory;
545     }
546     
547     /**
548      * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
549      * connections.  If the factory value is null, then a default
550      * factory is used (only do this to reset the factory after having
551      * previously altered it).
552      * <p>
553      * @param factory  The new ServerSocketFactory the SocketClient should use.
554      * @since 2.0
555      */
556     public void setServerSocketFactory(ServerSocketFactory factory) {
557         if (factory == null)
558             _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
559         else
560             _serverSocketFactory_ = factory;
561     }
562     
563     /**
564      * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
565      * connect() method. 
566      * @param connectTimeout The connection timeout to use (in ms)
567      * @since 2.0
568      */
569     public void setConnectTimeout(int connectTimeout) {
570         this.connectTimeout = connectTimeout;
571     }
572     
573     /**
574      * Get the underlying socket connection timeout.
575      * @return
576      * @since 2.0
577      */
578     public int getConnectTimeout() {
579         return connectTimeout;
580     }
581     
582     
583     
584 }
585 
586