Tag Archives: sftp

[Solved] Multithreading uses jsch to obtain a session for connection error: session.connect: java.net.socketexception: connection reset

Phenomenon

The project uses the spring batch framework. Multiple slices use jsch to obtain SFTP connections to read files and report errors

In fact, it is multithreading, using jsch to obtain the session connection and report an error

com.jcraft.jsch.JSchException: Session.connect: java.net.SocketException: Connection reset

Jsch version

version=0.1.54
groupId=com.jcraft
artifactId=jsch

reason

Various reasons have been found on the Internet. Some say the number of SSH terminal connections is limited, and some say there is a TCP connection problem. The final reason has not been found yet. Please inform us in the comment area

Reappearance

public static Session getSshSession(String sftpHost, int sftpPort, String userName, String password) {
	JSch jsch = new JSch();
	// GET sshSession
	Session sshSession = null;
	try {
		sshSession = jsch.getSession(userName, sftpHost, sftpPort);
	} catch (JSchException e) {
		e.printStackTrace();
	}
	if (StringUtils.isNotBlank(password)) {
		sshSession.setPassword(password);
	}
	Properties sshConfig = new Properties();
	sshConfig.put("StrictHostKeyChecking", "no");
	sshSession.setConfig(sshConfig);
	return sshSession;
}


static void test() {
	for (int i = 1; i < 50; i++) {
		new Thread(() -> {
			Session sshSession = getSshSession("*.*.*.*", 22, "root", "***");
			try {
				Thread.sleep(100);
				sshSession.connect();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				sshSession.disconnect();
			}

		}).start();
	}
}

Solution:

Create a channel pool using apache.commons.pool2

Since the SFTP configuration of the project is dynamic and not fixed, the following code is not encapsulated as a spring boot managed bean

Connection pool configuration:

public class ConnPoolConfig extends GenericObjectPoolConfig {
    public ConnPoolConfig() {
        // https://blog.csdn.net/weixin_42340670/article/details/108431381
        // The minimum number of free objects in the object pool should be
        setMinIdle(4);
        // The maximum capacity of the pool. The maximum number of objects to be stored in the pool
        setMaxTotal(10);
        // Check the validity of an object when it is borrowed from the pool.
        setTestOnBorrow(true);
        // How often the recycler thread performs idle object recovery (polling interval, in milliseconds)
        setTimeBetweenEvictionRunsMillis(60 * 60000);
        // Whether to verify the validity of the object when the recycler is scanning for idle objects.
        // If an object has not reached the specified threshold of idle time, and if testWhileIdle is configured to true
        // then it checks if the object is still valid, and if the object's resources have expired (e.g., the connection is disconnected), then he can be recycled.
        setTestWhileIdle(true);
    }
}

Connection pool factory:

public class ConnPoolFactory extends BasePooledObjectFactory<ChannelSftp> {

    private String host;
    private Integer port;
    private String userName;
    private String password;
    private final String strictHostKeyChecking = "no";

    public ConnPoolFactory(String host, Integer port, String userName, String password) {
        this.host = host;
        this.port = port;
        this.userName = userName;
        this.password = password;
    }

    @Override
    public ChannelSftp create() throws Exception {
        JSch jsch = new JSch();
        Session session = jsch.getSession(userName, host, port);
        session.setPassword(password);
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", strictHostKeyChecking);
        session.setConfig(config);
        session.connect();
        ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
        channel.connect();
        return channel;
    }

    @Override
    public PooledObject<ChannelSftp> wrap(ChannelSftp obj) {
        return new DefaultPooledObject<>(obj);
    }

    // https://segmentfault.com/a/1190000003920723
    // Destroy the object, if the object pool detects that an "object" idle timeout,
    // or if the operator detects that the "object" is no longer valid when "returning the object" to the object pool, then this will result in "object destruction";
    // The design of the "destroy object" operation is far different, but it must be clear:
    // When this method is called, the life of the "object" must end. If object is a thread, then the thread must exit at this point;
    // If object is a socket operation, then the socket must be closed;
    // If object is a file stream operation, then "data flush" is done and closed normally.
    @Override
    public void destroyObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
        Channel channel = pooledObject.getObject();
        Session session = channel.getSession();
        channel.disconnect();
        session.disconnect();
    }

    // Check if the object is "valid";
    // The Pool cannot hold invalid "objects", so the "background detection thread" will periodically check the validity of the "objects" in the Pool,
    // If the object is invalid, it will be removed from the Pool and destroyed;
    // In addition, when the caller gets an "object" from the Pool, it also checks the validity of the "object" to make sure that no "invalid" objects can be output to the caller;
    // When the caller returns the "object" to the Pool after use, the validity of the object is still checked. By validity,
    // The validity of the object is whether the object is in the expected state and can be used directly by the caller;
    // If the object is a socket, then its validity is whether the socket's channel is open/blocking timeout, etc.
    @Override
    public boolean validateObject(PooledObject<ChannelSftp> pooledObject) {
        return pooledObject.getObject().isConnected();
    }

    // "Activate" an object, an additional "activation" action when the Pool decides to remove an object for delivery to the caller,
    // For example, you can "reset" the list of parameters in the activateObject method to make it feel like a "newly created" object when the caller uses it;
    // If the object is a thread, you can reset the "thread break flag" in the "activate" operation, or wake up the thread from blocking, etc;
    // If the object is a socket, then you can refresh the channel in the "activate" operation,
    // or rebuild the link to the socket (if the socket is unexpectedly closed), etc.
    @Override
    public void activateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
        ChannelSftp channelSftp = pooledObject.getObject();
        Session session = channelSftp.getSession();
        if (!session.isConnected()) {
            session.connect();
            channelSftp.connect();
        }
    }

    // "Passivate" the object, when the caller "returns the object", the Pool will "passivate the object".
    // The implication of passivate is that the "object" needs a "rest" for a while.
    // If the object is a socket, then you can passivateObject to clear the buffer and block the socket;
    // If the object is a thread, you can sleep the thread or wait for an object in the thread during the "passivate" operation.
    // Note that the methods activateObject and passivateObject need to correspond to each other to avoid deadlocks or confusion about the state of the "object".
    @Override
    public void passivateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
    }
}

Connection pool:

public class ConnPool extends GenericObjectPool<ChannelSftp> {

    private static final Map<String, ConnPool> MAP = new ConcurrentHashMap<>();

    private ConnPool(String host, Integer port, String userName, String password) {
        super(new ConnPoolFactory(host, port, userName, password), new ConnPoolConfig());
    }

    public static ConnPool getConnPool(String host, Integer port, String userName, String password) {
        String key = host + ":" + port;
        ConnPool connPool = MAP.get(key);
        if (connPool == null) {
            synchronized (ConnPool.class) {
                connPool = MAP.get(key);
                if (connPool == null) {
                    connPool = new ConnPool(host, port, userName, password);
                    MAP.put(key, connPool);
                }
            }
        }
        return connPool;
    }
}

The connection pool supports the establishment of different pools for different remote IP

Tool class encapsulation:

public static ChannelSftp borrowChannel(ConnectionConfig connCfg) {
	ConnPool connPool = ConnPool.getConnPool(connCfg.getHost(), connCfg.getPort(), connCfg.getUserName(),
			connCfg.getPassword());
	try {
		return connPool.borrowObject();
	} catch (Exception e) {
		logger.error("Get channelSftp from pool fail", e);
	}
}

public static void returnChannel(ConnectionConfig connCfg, ChannelSftp channel) {
	ConnPool connPool = ConnPool.getConnPool(connCfg.getHost(), connCfg.getPort(), connCfg.getUserName(),
			connCfg.getPassword());
	try {
		connPool.returnObject(channel);
	} catch (Exception e) {
		logger.error("Return channelSftp to pool fail", e);
	}
}

No problem with the test:

static void test2() {
	AtomicInteger j = new AtomicInteger(0);
	for (int i = 0; i < 50; i++) {
		new Thread(() -> {
			ConnPool connPool = ConnPool.getConnPool("*", 22, "root", "*");
			System.out.println(connPool + "--" + j.getAndIncrement());
			ChannelSftp channelSftp = null;
			try {
				channelSftp = connPool.borrowObject();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				connPool.returnObject(channelSftp);
			}
		}).start();
	}
}

[Solved] paramiko.ssh_exception.SSHException: Error reading SSH protocol banner

Recently, SFTP upload and download should be configured in the project. After the environment is configured, the connection will report an error.

The error information is as follows:

paramiko.ssh_exception.SSHException: Error reading SSH protocol banner

Solution 1:

Set banner_ Timeout property value

You can change the transport.py under the paramiko Library in the site packages source file. The transport code is as follows:

 class Transport(threading.Thread, ClosingContextManager):
        self.banner_timeout = 15
        # how long (seconds) to wait for the handshake to finish after SSH

15 can be changed to 30

Solution 2:

Pay attention to check your host, port, username, password and other information, as well as upload or download directory path!

Linux configuration SFTP server

Add user group

groupadd sftp

Add users and set to SFTP group

sudo useradd -g sftp -s /sbin/nologin -M sftp

Modify the SFTP user’s password

sudo passwd sftp

***********

Create the root directory of the SFTP user and set the owner and group, modify the permissions (755)

cd /home
sudo mkdir sftp

sudo chown root:sftp sftp

sudo chmod 755 sftp

Create admin writable directory
in the directory of SFTP

cd sftp
sudo mkdir report

sudo chown admin:sftp report/

Modify the configuration file

sudo vim /etc/ssh/sshd_config

Modify the

#Subsystem      sftp    /usr/libexec/openssh/sftp-server
Subsystem       sftp    internal-sftp

Add at the end of the sshd_config file

Match User sftp
        X11Forwarding no
        AllowTcpForwarding no
        ForceCommand internal-sftp
        ChrootDirectory /home/sftp

Restart the SSHD service

sudo service sshd restart

ftp client:

address : 192.168.1.10

user: sftp

password: xxxxxxx

protocol type : SSH2

port: 22

server folder: /home/sftp

pls use CuteFTP Pro® 8.3.3 or last version.

because CuteFTP Pro® 8.3.2 or earlier has no AES128 and AES256 ciphers.

will raise throw “SFTP21 error = #4” .

On fileZilla unable to connect FATAL ERROR: Connection union

FileZilla is approximately equal to WinSCP for MAC

 
 
 
 
In the figure, the host is [FTP, SFTP..] :// Remote IP address, the user password is entered correctly, then the port used a port 80 (already open), the result of a run report error.
Change the port to another port, but still report an error.
Then think of the next, other software transmission is more than 22 port, so change to 22 port (has been opened), connected successfully!
 
Reason: Unknown