/*
* Copyright 2001-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
/**
* 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 the same code over and over again
* to open a socket, close a socket, set timeouts, etc. Of special note
* is the setSocketFactory
* method, which allows you to control the type of Socket the SocketClient
* creates for initiating network connections. This is especially useful
* for adding SSL or proxy support as well as better support for applets. For
* example, you could create a
* SocketFactory that
* requests browser security capabilities before creating a socket.
* All classes derived from SocketClient should use the
* _socketFactory_ member variable to
* create Socket and ServerSocket instances rather than instanting
* them by directly invoking a constructor. By honoring this contract
* you guarantee that a user will always be able to provide his own
* Socket implementations by substituting his own SocketFactory.
* @author Daniel F. Savarese
* @see SocketFactory
*/
public abstract class SocketClient
{
/**
* The end of line character sequence used by most IETF protocols. That
* is a carriage return followed by a newline: "\r\n"
*/
public static final String NETASCII_EOL = "\r\n";
/** The default SocketFactory shared by all SocketClient instances. */
private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
new DefaultSocketFactory();
/** The timeout to use after opening a socket. */
protected int _timeout_;
/** The socket used for the connection. */
protected Socket _socket_;
/**
* A status variable indicating if the client's socket is currently open.
*/
protected boolean _isConnected_;
/** The default port the client should connect to. */
protected int _defaultPort_;
/** The socket's InputStream. */
protected InputStream _input_;
/** The socket's OutputStream. */
protected OutputStream _output_;
/** The socket's SocketFactory. */
protected SocketFactory _socketFactory_;
/**
* Default constructor for SocketClient. Initializes
* _socket_ to null, _timeout_ to 0, _defaultPort to 0,
* _isConnected_ to false, and _socketFactory_ to a shared instance of
* DefaultSocketFactory .
*/
public SocketClient()
{
_socket_ = null;
_input_ = null;
_output_ = null;
_timeout_ = 0;
_defaultPort_ = 0;
_isConnected_ = false;
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
}
/**
* Because there are so many connect() methods, the _connectAction_()
* method is provided as a means of performing some action immediately
* after establishing a connection, rather than reimplementing all
* of the connect() methods. The last action performed by every
* connect() method after opening a socket is to call this method.
*
* This method sets the timeout on the just opened socket to the default
* timeout set by setDefaultTimeout() ,
* sets _input_ and _output_ to the socket's InputStream and OutputStream
* respectively, and sets _isConnected_ to true.
*
* Subclasses overriding this method should start by calling
* super._connectAction_()
first to ensure the
* initialization of the aforementioned protected variables.
*/
protected void _connectAction_() throws IOException
{
_socket_.setSoTimeout(_timeout_);
_input_ = _socket_.getInputStream();
_output_ = _socket_.getOutputStream();
_isConnected_ = true;
}
/**
* 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, _connectAction_()
* is called to perform connection initialization actions.
*
* @param host The remote host.
* @param port The port to connect to on the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host, int port)
throws SocketException, IOException
{
_socket_ = _socketFactory_.createSocket(host, port);
_connectAction_();
}
/**
* 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, _connectAction_()
* is called to perform connection initialization actions.
*
* @param hostname The name of the remote host.
* @param port The port to connect to on the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname, int port)
throws SocketException, IOException
{
_socket_ = _socketFactory_.createSocket(hostname, port);
_connectAction_();
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the specified local address and port.
* Before returning, _connectAction_()
* is called to perform connection initialization actions.
*
* @param host The remote host.
* @param port The port to connect to on the remote host.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host, int port,
InetAddress localAddr, int localPort)
throws SocketException, IOException
{
_socket_ = _socketFactory_.createSocket(host, port, localAddr, localPort);
_connectAction_();
}
/**
* Opens a Socket connected to a remote host at the specified port and
* originating from the specified local address and port.
* Before returning, _connectAction_()
* is called to perform connection initialization actions.
*
* @param hostname The name of the remote host.
* @param port The port to connect to on the remote host.
* @param localAddr The local address to use.
* @param localPort The local port to use.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname, int port,
InetAddress localAddr, int localPort)
throws SocketException, IOException
{
_socket_ =
_socketFactory_.createSocket(hostname, port, localAddr, localPort);
_connectAction_();
}
/**
* 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, _connectAction_()
* is called to perform connection initialization actions.
*
* @param host The remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
*/
public void connect(InetAddress host) throws SocketException, IOException
{
connect(host, _defaultPort_);
}
/**
* 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, _connectAction_()
* is called to perform connection initialization actions.
*
* @param hostname The name of the remote host.
* @exception SocketException If the socket timeout could not be set.
* @exception IOException If the socket could not be opened. In most
* cases you will only want to catch IOException since SocketException is
* derived from it.
* @exception UnknownHostException If the hostname cannot be resolved.
*/
public void connect(String hostname) throws SocketException, IOException
{
connect(hostname, _defaultPort_);
}
/**
* Disconnects the socket connection.
* You should call this method after you've finished using the class
* instance and also before you call
* connect()
* again. _isConnected_ is set to false, _socket_ is set to null,
* _input_ is set to null, and _output_ is set to null.
*
* @exception IOException If there is an error closing the socket.
*/
public void disconnect() throws IOException
{
_socket_.close();
_input_.close();
_output_.close();
_socket_ = null;
_input_ = null;
_output_ = null;
_isConnected_ = false;
}
/**
* Returns true if the client is currently connected to a server.
*
* @return True if the client is currently connected to a server,
* false otherwise.
*/
public boolean isConnected()
{
return _isConnected_;
}
/**
* Sets the default port the SocketClient should connect to when a port
* is not specified. The _defaultPort_
* variable stores this value. If never set, the default port is equal
* to zero.
*
* @param port The default port to set.
*/
public void setDefaultPort(int port)
{
_defaultPort_ = port;
}
/**
* Returns the current value of the default port (stored in
* _defaultPort_ ).
*
* @return The current value of the default port.
*/
public int getDefaultPort()
{
return _defaultPort_;
}
/**
* Set the default timeout in milliseconds to use when opening a socket.
* This value is only used previous to a call to
* connect()
* and should not be confused with setSoTimeout()
* which operates on an the currently opened socket. _timeout_ contains
* the new timeout value.
*
* @param timeout The timeout in milliseconds to use for the socket
* connection.
*/
public void setDefaultTimeout(int timeout)
{
_timeout_ = timeout;
}
/**
* Returns the default timeout in milliseconds that is used when
* opening a socket.
*
* @return The default timeout in milliseconds that is used when
* opening a socket.
*/
public int getDefaultTimeout()
{
return _timeout_;
}
/**
* Set the timeout in milliseconds of a currently open connection.
* Only call this method after a connection has been opened
* by connect().
*
* @param timeout The timeout in milliseconds to use for the currently
* open socket connection.
* @exception SocketException If the operation fails.
*/
public void setSoTimeout(int timeout) throws SocketException
{
_socket_.setSoTimeout(timeout);
}
/**
* Returns the timeout in milliseconds of the currently opened socket.
*
* @return The timeout in milliseconds of the currently opened socket.
* @exception SocketException If the operation fails.
*/
public int getSoTimeout() throws SocketException
{
return _socket_.getSoTimeout();
}
/**
* Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
* currently opened socket.
*
* @param on True if Nagle's algorithm is to be enabled, false if not.
* @exception SocketException If the operation fails.
*/
public void setTcpNoDelay(boolean on) throws SocketException
{
_socket_.setTcpNoDelay(on);
}
/**
* Returns true if Nagle's algorithm is enabled on the currently opened
* socket.
*
* @return True if Nagle's algorithm is enabled on the currently opened
* socket, false otherwise.
* @exception SocketException If the operation fails.
*/
public boolean getTcpNoDelay() throws SocketException
{
return _socket_.getTcpNoDelay();
}
/**
* Sets the SO_LINGER timeout on the currently opened socket.
*
* @param on True if linger is to be enabled, false if not.
* @param val The linger timeout (in hundredths of a second?)
* @exception SocketException If the operation fails.
*/
public void setSoLinger(boolean on, int val) throws SocketException
{
_socket_.setSoLinger(on, val);
}
/**
* Returns the current SO_LINGER timeout of the currently opened socket.
*
* @return The current SO_LINGER timeout. If SO_LINGER is disabled returns
* -1.
* @exception SocketException If the operation fails.
*/
public int getSoLinger() throws SocketException
{
return _socket_.getSoLinger();
}
/**
* Returns the port number of the open socket on the local host used
* for the connection.
*
* @return The port number of the open socket on the local host used
* for the connection.
*/
public int getLocalPort()
{
return _socket_.getLocalPort();
}
/**
* Returns the local address to which the client's socket is bound.
*
* @return The local address to which the client's socket is bound.
*/
public InetAddress getLocalAddress()
{
return _socket_.getLocalAddress();
}
/**
* Returns the port number of the remote host to which the client is
* connected.
*
* @return The port number of the remote host to which the client is
* connected.
*/
public int getRemotePort()
{
return _socket_.getPort();
}
/**
* @return The remote address to which the client is connected.
*/
public InetAddress getRemoteAddress()
{
return _socket_.getInetAddress();
}
/**
* Verifies that the remote end of the given socket is connected to the
* the same host that the SocketClient is currently connected to. This
* is useful for 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 stream.
*
* @return True if the remote hosts are the same, false if not.
*/
public boolean verifyRemote(Socket socket)
{
InetAddress host1, host2;
host1 = socket.getInetAddress();
host2 = getRemoteAddress();
return host1.equals(host2);
}
/**
* 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 reset the factory after having
* previously altered it).
*
* @param factory The new SocketFactory the SocketClient should use.
*/
public void setSocketFactory(SocketFactory factory)
{
if (factory == null)
_socketFactory_ = __DEFAULT_SOCKET_FACTORY;
else
_socketFactory_ = factory;
}
}