All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.feilong.lib.net.SocketClient Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.feilong.lib.net;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketException;
import java.nio.charset.Charset;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;

/**
 * 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 {@link #setSocketFactory 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
 * {@link javax.net.SocketFactory} that
 * requests browser security capabilities before creating a socket.
 * All classes derived from SocketClient should use the
 * {@link #_socketFactory_ _socketFactory_ } member variable to
 * create Socket and ServerSocket instances rather than instantiating
 * 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.
 * 
 * @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        = SocketFactory.getDefault();

    /** The default {@link ServerSocketFactory} */
    private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = ServerSocketFactory.getDefault();

    /**
     * A ProtocolCommandSupport object used to manage the registering of
     * ProtocolCommandListeners and the firing of ProtocolCommandEvents.
     */
    private ProtocolCommandSupport           __commandSupport;

    /** The timeout to use after opening a socket. */
    protected int                            _timeout_;

    /** The socket used for the connection. */
    protected Socket                         _socket_;

    /** The hostname used for the connection (null = no hostname supplied). */
    protected String                         _hostname_;

    /** 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_;

    /** The socket's ServerSocket Factory. */
    protected ServerSocketFactory            _serverSocketFactory_;

    /** The socket's connect timeout (0 = infinite timeout) */
    private static final int                 DEFAULT_CONNECT_TIMEOUT         = 0;

    protected int                            connectTimeout                  = DEFAULT_CONNECT_TIMEOUT;

    /** Hint for SO_RCVBUF size */
    private int                              receiveBufferSize               = -1;

    /** Hint for SO_SNDBUF size */
    private int                              sendBufferSize                  = -1;

    /** The proxy to use when connecting. */
    private Proxy                            connProxy;

    /**
     * Charset to use for byte IO.
     */
    private Charset                          charset                         = Charset.defaultCharset();

    /**
     * Default constructor for SocketClient. Initializes
     * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
     * _isConnected_ to false, charset to {@code Charset.defaultCharset()}
     * and _socketFactory_ to a shared instance of
     * {@link com.feilong.lib.net.DefaultSocketFactory}.
     */
    public SocketClient(){
        _socket_ = null;
        _hostname_ = null;
        _input_ = null;
        _output_ = null;
        _timeout_ = 0;
        _defaultPort_ = 0;
        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
        _serverSocketFactory_ = __DEFAULT_SERVER_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 {@link #setDefaultTimeout 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. * * @throws IOException * (SocketException) if a problem occurs with the socket */ protected void _connectAction_() throws IOException{ _socket_.setSoTimeout(_timeout_); _input_ = _socket_.getInputStream(); _output_ = _socket_.getOutputStream(); } /** * 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, {@link #_connectAction_ _connectAction_() } * is called to perform connection initialization actions. *

* * @param host * The remote host. * @param port * The port to connect to on the remote host. * @throws SocketException * If the socket timeout could not be set. * @throws 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{ _hostname_ = null; _connect(host, port, null, -1); } /** * 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, {@link #_connectAction_ _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. * @throws SocketException * If the socket timeout could not be set. * @throws IOException * If the socket could not be opened. In most * cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.UnknownHostException * If the hostname cannot be resolved. */ public void connect(String hostname,int port) throws SocketException,IOException{ _hostname_ = hostname; _connect(InetAddress.getByName(hostname), port, null, -1); } /** * Opens a Socket connected to a remote host at the specified port and * originating from the specified local address and port. * Before returning, {@link #_connectAction_ _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. * @throws SocketException * If the socket timeout could not be set. * @throws 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{ _hostname_ = null; _connect(host, port, localAddr, localPort); } // helper method to allow code to be shared with connect(String,...) methods private void _connect(InetAddress host,int port,InetAddress localAddr,int localPort) throws SocketException,IOException{ _socket_ = _socketFactory_.createSocket(); if (receiveBufferSize != -1){ _socket_.setReceiveBufferSize(receiveBufferSize); } if (sendBufferSize != -1){ _socket_.setSendBufferSize(sendBufferSize); } if (localAddr != null){ _socket_.bind(new InetSocketAddress(localAddr, localPort)); } _socket_.connect(new InetSocketAddress(host, port), connectTimeout); _connectAction_(); } /** * Opens a Socket connected to a remote host at the specified port and * originating from the specified local address and port. * Before returning, {@link #_connectAction_ _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. * @throws SocketException * If the socket timeout could not be set. * @throws IOException * If the socket could not be opened. In most * cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.UnknownHostException * If the hostname cannot be resolved. */ public void connect(String hostname,int port,InetAddress localAddr,int localPort) throws SocketException,IOException{ _hostname_ = hostname; _connect(InetAddress.getByName(hostname), port, localAddr, localPort); } /** * 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, {@link #_connectAction_ _connectAction_() } * is called to perform connection initialization actions. *

* * @param host * The remote host. * @throws SocketException * If the socket timeout could not be set. * @throws 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{ _hostname_ = null; 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, {@link #_connectAction_ _connectAction_() } * is called to perform connection initialization actions. *

* * @param hostname * The name of the remote host. * @throws SocketException * If the socket timeout could not be set. * @throws IOException * If the socket could not be opened. In most * cases you will only want to catch IOException since SocketException is * derived from it. * @throws java.net.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 * {@link #connect connect() } * again. _isConnected_ is set to false, _socket_ is set to null, * _input_ is set to null, and _output_ is set to null. *

* * @throws IOException * If there is an error closing the socket. */ public void disconnect() throws IOException{ closeQuietly(_socket_); closeQuietly(_input_); closeQuietly(_output_); _socket_ = null; _hostname_ = null; _input_ = null; _output_ = null; } private static void closeQuietly(Socket socket){ if (socket != null){ try{ socket.close(); }catch (IOException e){ // Ignored } } } private static void closeQuietly(Closeable close){ if (close != null){ try{ close.close(); }catch (IOException e){ // Ignored } } } /** * Returns true if the client is currently connected to a server. *

* Delegates to {@link Socket#isConnected()} * * @return True if the client is currently connected to a server, * false otherwise. */ public boolean isConnected(){ if (_socket_ == null){ return false; } return _socket_.isConnected(); } /** * Make various checks on 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. * * @see NET-350 * @return {@code true} if the socket appears to be available for use * @since 3.0 */ public boolean isAvailable(){ if (isConnected()){ try{ if (_socket_.getInetAddress() == null){ return false; } if (_socket_.getPort() == 0){ return false; } if (_socket_.getRemoteSocketAddress() == null){ return false; } if (_socket_.isClosed()){ return false; } /* * 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: */ if (_socket_.isInputShutdown()){ return false; } if (_socket_.isOutputShutdown()){ return false; } /* ignore the result, catch exceptions: */ _socket_.getInputStream(); _socket_.getOutputStream(); }catch (IOException ioex){ return false; } return true; } return false; } /** * Sets the default port the SocketClient should connect to when a port * is not specified. The {@link #_defaultPort_ _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 * {@link #_defaultPort_ _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 * {@link #connect connect()} * and should not be confused with {@link #setSoTimeout 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 {@link #connect connect()}. *

* To set the initial timeout, use {@link #setDefaultTimeout(int)} instead. * * @param timeout * The timeout in milliseconds to use for the currently * open socket connection. * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ public void setSoTimeout(int timeout) throws SocketException{ _socket_.setSoTimeout(timeout); } /** * Set the underlying socket send buffer size. *

* * @param size * The size of the buffer in bytes. * @throws SocketException * never thrown, but subclasses might want to do so * @since 2.0 */ public void setSendBufferSize(int size) throws SocketException{ sendBufferSize = size; } /** * Get the current sendBuffer size * * @return the size, or -1 if not initialised * @since 3.0 */ protected int getSendBufferSize(){ return sendBufferSize; } /** * Sets the underlying socket receive buffer size. *

* * @param size * The size of the buffer in bytes. * @throws SocketException * never (but subclasses may wish to do so) * @since 2.0 */ public void setReceiveBufferSize(int size) throws SocketException{ receiveBufferSize = size; } /** * Get the current receivedBuffer size * * @return the size, or -1 if not initialised * @since 3.0 */ protected int getReceiveBufferSize(){ return receiveBufferSize; } /** * Returns the timeout in milliseconds of the currently opened socket. *

* * @return The timeout in milliseconds of the currently opened socket. * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ 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. * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ 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. * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ public boolean getTcpNoDelay() throws SocketException{ return _socket_.getTcpNoDelay(); } /** * Sets the SO_KEEPALIVE flag on the currently opened socket. * * From the Javadocs, the default keepalive time is 2 hours (although this is * implementation dependent). It looks as though the Windows WSA sockets implementation * allows a specific keepalive value to be set, although this seems not to be the case on * other systems. * * @param keepAlive * If true, keepAlive is turned on * @throws SocketException * if there is a problem with the socket * @throws NullPointerException * if the socket is not currently open * @since 2.2 */ public void setKeepAlive(boolean keepAlive) throws SocketException{ _socket_.setKeepAlive(keepAlive); } /** * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket. * Delegates to {@link Socket#getKeepAlive()} * * @return True if SO_KEEPALIVE is enabled. * @throws SocketException * if there is a problem with the socket * @throws NullPointerException * if the socket is not currently open * @since 2.2 */ public boolean getKeepAlive() throws SocketException{ return _socket_.getKeepAlive(); } /** * 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?) * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ 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. * @throws SocketException * If the operation fails. * @throws NullPointerException * if the socket is not currently open */ public int getSoLinger() throws SocketException{ return _socket_.getSoLinger(); } /** * Returns the port number of the open socket on the local host used * for the connection. * Delegates to {@link Socket#getLocalPort()} *

* * @return The port number of the open socket on the local host used * for the connection. * @throws NullPointerException * if the socket is not currently open */ public int getLocalPort(){ return _socket_.getLocalPort(); } /** * Returns the local address to which the client's socket is bound. * Delegates to {@link Socket#getLocalAddress()} *

* * @return The local address to which the client's socket is bound. * @throws NullPointerException * if the socket is not currently open */ public InetAddress getLocalAddress(){ return _socket_.getLocalAddress(); } /** * Returns the port number of the remote host to which the client is * connected. * Delegates to {@link Socket#getPort()} *

* * @return The port number of the remote host to which the client is * connected. * @throws NullPointerException * if the socket is not currently open */ public int getRemotePort(){ return _socket_.getPort(); } /** * @return The remote address to which the client is connected. * Delegates to {@link Socket#getInetAddress()} * @throws NullPointerException * if the socket is not currently open */ 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. *

* * @param socket * the item to check against * @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). * Any proxy setting is discarded. *

* * @param factory * The new SocketFactory the SocketClient should use. */ public void setSocketFactory(SocketFactory factory){ if (factory == null){ _socketFactory_ = __DEFAULT_SOCKET_FACTORY; }else{ _socketFactory_ = factory; } // re-setting the socket factory makes the proxy setting useless, // so set the field to null so that getProxy() doesn't return a // Proxy that we're actually not using. connProxy = null; } /** * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket * 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 ServerSocketFactory the SocketClient should use. * @since 2.0 */ public void setServerSocketFactory(ServerSocketFactory factory){ if (factory == null){ _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY; }else{ _serverSocketFactory_ = factory; } } /** * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's * connect() method. * * @param connectTimeout * The connection timeout to use (in ms) * @since 2.0 */ public void setConnectTimeout(int connectTimeout){ this.connectTimeout = connectTimeout; } /** * Get the underlying socket connection timeout. * * @return timeout (in ms) * @since 2.0 */ public int getConnectTimeout(){ return connectTimeout; } /** * Get the underlying {@link ServerSocketFactory} * * @return The server socket factory * @since 2.2 */ public ServerSocketFactory getServerSocketFactory(){ return _serverSocketFactory_; } /** * Adds a ProtocolCommandListener. * * @param listener * The ProtocolCommandListener to add. * @since 3.0 */ public void addProtocolCommandListener(ProtocolCommandListener listener){ getCommandSupport().addProtocolCommandListener(listener); } /** * Removes a ProtocolCommandListener. * * @param listener * The ProtocolCommandListener to remove. * @since 3.0 */ public void removeProtocolCommandListener(ProtocolCommandListener listener){ getCommandSupport().removeProtocolCommandListener(listener); } /** * If there are any listeners, send them the reply details. * * @param replyCode * the code extracted from the reply * @param reply * the full reply text * @since 3.0 */ protected void fireReplyReceived(int replyCode,String reply){ if (getCommandSupport().getListenerCount() > 0){ getCommandSupport().fireReplyReceived(replyCode, reply); } } /** * If there are any listeners, send them the command details. * * @param command * the command name * @param message * the complete message, including command name * @since 3.0 */ protected void fireCommandSent(String command,String message){ if (getCommandSupport().getListenerCount() > 0){ getCommandSupport().fireCommandSent(command, message); } } /** * Create the CommandSupport instance if required */ protected void createCommandSupport(){ __commandSupport = new ProtocolCommandSupport(this); } /** * Subclasses can override this if they need to provide their own * instance field for backwards compatibilty. * * @return the CommandSupport instance, may be {@code null} * @since 3.0 */ protected ProtocolCommandSupport getCommandSupport(){ return __commandSupport; } /** * Sets the proxy for use with all the connections. * The proxy is used for connections established after the * call to this method. * * @param proxy * the new proxy for connections. * @since 3.2 */ public void setProxy(Proxy proxy){ setSocketFactory(new DefaultSocketFactory(proxy)); connProxy = proxy; } /** * Gets the proxy for use with all the connections. * * @return the current proxy for connections. */ public Proxy getProxy(){ return connProxy; } /** * Gets the charset name. * * @return the charset. * @since 3.3 * @deprecated Since the code now requires Java 1.6 as a mininmum */ @Deprecated public String getCharsetName(){ return charset.name(); } /** * Gets the charset. * * @return the charset. * @since 3.3 */ public Charset getCharset(){ return charset; } /** * Sets the charset. * * @param charset * the charset. * @since 3.3 */ public void setCharset(Charset charset){ this.charset = charset; } /* * N.B. 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 methods which were moved here. */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy