多线程用jsch获取session进行连接,报错Session.connect: java.net.SocketException: Connection reset

JSch Java SSH开发实战:连接池、密钥认证与生产级故障排查 SSH 是 Linux 系统远程管理的核心协议,而 JSch 作为纯 Java 实现的 SSH 客户端库,解决了 Runtime.exec 调用系统 ssh 命令带来的环境依赖、权限失控和连接不可控等工程痛点。其底层基于 RFC 4251–4254 协议栈,支持会话复用、通道管理、密钥交换与流式 I/O 控制,具备企业级可监控、可熔断、可审计的技术能力。在自动化运维、金融对账、CI/CD 执行等场景中,JSch 被广泛用于安全执行远程命令、SFTP 文件传输及 SSH 端口转发。本文聚焦 JSch 的生产就 阅读详情
AI摘要

本文解决JSch框架在多线程环境下频繁创建SFTP连接导致的连接重置问题,通过使用Apache Commons Pool2创建连接池,实现对不同远程IP的SFTP连接的有效管理和复用。

现象

项目使用springbatch框架,多个分片使用jsch获取sftp连接去读文件,报错

其实就是进行多线程用jsch获取session连接,报错

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

jsch版本

version=0.1.54
groupId=com.jcraft
artifactId=jsch

原因

网上找了各种原因,有说ssh终端连接个数限制,有说tcp的连接问题,暂未找到最终原因,欢迎评论区告知

复现

public static Session getSshSession(String sftpHost, int sftpPort, String userName, String password) {
	// 实例化
	JSch jsch = new JSch();
	// 获取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();
	}
}

 

解决方案

使用apache.commons.pool2创建channel池

支持针对不同的远程ip建立不同的连接池

由于项目的sftp链接页面可配置,因此未封装为spring管理的bean

连接池配置:

public class ConnPoolConfig extends GenericObjectPoolConfig {
    public ConnPoolConfig() {
        // https://blog.youkuaiyun.com/weixin_42340670/article/details/108431381
        // 对象池中最少需要有几个空闲对象
        setMinIdle(4);
        // 对象池的最大容量。池里最多存放多少个对象
        setMaxTotal(10);
        // 当从对象池里借走一个对象的时候,是否校验该对象的有效性
        setTestOnBorrow(true);
        // 回收器线程多久执行一次空闲对象回收(轮询间隔时间,单位毫秒)
        setTimeBetweenEvictionRunsMillis(60 * 60000);
        // 当回收器在扫描空闲对象时,是否校验对象的有效性。
        // 如果某个对象空闲时间还没达到规定的阈值,如果testWhileIdle配置为true,
        // 那么就会检查该对象是否还有效,如果该对象的资源已经失效(例如:连接断开),那么他就可以被回收。
        setTestWhileIdle(true);
    }
}

连接池工厂:

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
    // 销毁对象,如果对象池中检测到某个"对象"idle的时间超时,
    // 或者操作者向对象池"归还对象"时检测到"对象"已经无效,那么此时将会导致"对象销毁";
    // "销毁对象"的操作设计相差甚远,但是必须明确:
    // 当调用此方法时,"对象"的生命周期必须结束.如果object是线程,那么此时线程必须退出;
    // 如果object是socket操作,那么此时socket必须关闭;
    // 如果object是文件流操作,那么此时"数据flush"且正常关闭.
    @Override
    public void destroyObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
        Channel channel = pooledObject.getObject();
        Session session = channel.getSession();
        channel.disconnect();
        session.disconnect();
    }

    // 检测对象是否"有效";
    // Pool中不能保存无效的"对象",因此"后台检测线程"会周期性的检测Pool中"对象"的有效性,
    // 如果对象无效则会导致此对象从Pool中移除,并destroy;
    // 此外在调用者从Pool获取一个"对象"时,也会检测"对象"的有效性,确保不能讲"无效"的对象输出给调用者;
    // 当调用者使用完毕将"对象归还"到Pool时,仍然会检测对象的有效性.所谓有效性,
    // 就是此"对象"的状态是否符合预期,是否可以对调用者直接使用;
    // 如果对象是Socket,那么它的有效性就是socket的通道是否畅通/阻塞是否超时等.
    @Override
    public boolean validateObject(PooledObject<ChannelSftp> pooledObject) {
        return pooledObject.getObject().isConnected();
    }

    // "激活"对象,当Pool中决定移除一个对象交付给调用者时额外的"激活"操作,
    // 比如可以在activateObject方法中"重置"参数列表让调用者使用时感觉像一个"新创建"的对象一样;
    // 如果object是一个线程,可以在"激活"操作中重置"线程中断标记",或者让线程从阻塞中唤醒等;
    // 如果object是一个socket,那么可以在"激活操作"中刷新通道,
    // 或者对socket进行链接重建(假如socket意外关闭)等.
    @Override
    public void activateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
        ChannelSftp channelSftp = pooledObject.getObject();
        Session session = channelSftp.getSession();
        if (!session.isConnected()) {
            session.connect();
            channelSftp.connect();
        }
    }

    // "钝化"对象,当调用者"归还对象"时,Pool将会"钝化对象";
    // 钝化的言外之意,就是此"对象"暂且需要"休息"一下.
    // 如果object是一个socket,那么可以passivateObject中清除buffer,将socket阻塞;
    // 如果object是一个线程,可以在"钝化"操作中将线程sleep或者将线程中的某个对象wait.
    // 需要注意的时,activateObject和passivateObject两个方法需要对应,避免死锁或者"对象"状态的混乱.
    @Override
    public void passivateObject(PooledObject<ChannelSftp> pooledObject) throws Exception {
    }
}

连接池:

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;
    }
}

工具类封装:

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);
	}
}

测试无问题:

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();
	}
}

高吞吐SFTP连接池设计方案 我们如果使用这种可配置连接池进行访问,对接上游时最好时最好跟上游确认他们服务器可以承受的Session数量和Channel数量是多少,宁愿少配也不要多配。但是对于一般上游,如果使用的是Linux服务器,默认值就是上面一开始提到的,我们客户端连接池保守配置可以设置成,这样子最大并行可以处理30个文件,MaxSession一般不建议设置太多,非常消耗系统资源。也有很多上游是使用其他SFTP服务管理工具,但是基本限制参数差不多。以上连接池还有很多需要优化的地方,大家可以根据需要自己去优化。 阅读详情

相关推荐

针对java.net.SocketException: Connection reset的问题排查

最近在与第三方系统对接接口时,需要用到 socket这种方式,在调试过程中,一直出现java.net.SocketException: Connection reset这个错误 我的客户端代码 import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; public class ClientDemo1 { public sta...

ll6677_的博客 8570

解决Session.connect: java.net.SocketException: Connection reset

原本之前就配置好了FinalShell可以正常连接虚拟机,突然修改了某些IP或有一天重新连接却连接不上了

JIE_PLAY的博客 1万+

如何解决 java.net.SocketTimeoutException: connect timed out...【亲测有效】

记录:java.net.SocketTimeoutException: connect timed out...解决方案。

**My Coding Family** 1万+

java.net.SocketException: Connection reset 异常原因分析和解决方法

Connection reset : 一般是客户端正在从服务器读数据时或者向服务器写数据时,服务器连接关闭,通过tcpdump抓包可以看到,返回了一个RST复位标志,导致连接重置。 导致此异常的原因,总结下来有三种情况: 1.服务器端偶尔出现了异常,导致连接关闭 解决方法: 采用出错重试...

chicaohun7473的博客 4万+

西门子828D/840DSL数控机床产量数据采集实战:手把手教你绕过授权限制(附Demo源码)

本文详细介绍了西门子828D/840DSL数控机床产量数据采集的实战技术,包括绕过授权限制的替代方案和实现方法。通过分析通信协议、配置参数和数据处理,提供了一套高效且合规的数据采集解决方案,并附有Demo源码,帮助技术人员快速上手。

weixin_28278377的博客 762

基于FPGA的CORDIC算法实现——Verilog版

目前,学习与开发FPGA的程序员们大多使用的是Verilog HDL语言(以下简称为Verilog),关于Verilog的诸多优点一休哥就不多介绍了,在此,我们将重点放在Verilog的运算操作上。 我们都知道,在Verilog中,运算一般分为逻辑运算(与或非等)与算术运算(加减乘除等)。而在一开始学习Verilog时,老司机一定会提醒我们,“切记,千万别用‘/’除、‘%’取模(有的也叫取余)和‘

善良の一休君的博客 6万+

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

sftp上传服务器没报错,但是图片清空会显示com.jcraft.jsch.JSchException:Session.connect:java.net.SocketException:Connectionreset 原因是因为加了黑白名单 解决方式就是新增dockertomcat容器的ipsshd:172.21.0.3/32:allow到"/etc/hosts.allow"里面(允许访问本机的客户端列表),ip可能会变,但是等保不允许加入一个网段,等等保过了可以新增允许一个网段sshd:...

qq_26891571的博客 7780

【微知】关于GB200 NVL72服务器的一些信息?(36CPU72GPU;LPDDR5X 17T;HBM3e 13.5T;5代NVlink 1.8TB; 720P算力FP8;120Kw)

本文仅根据官网介绍分析,存在纰漏仅供参考。

技术札记 5386

java.net.SocketException:Connection reset

这种情况下,Java 程序在尝试与服务器建立连接或发送数据时,服务器端可能已经关闭了连接,导致连接被重置。如果 Java 程序在建立连接后,在规定时间内没有发送数据,服务器可能会主动关闭连接,导致连接重置。如果有发送数据的操作,在建立连接后适当设置数据发送超时,避免在规定时间内没有发送数据导致连接被重置。确认发送的数据包大小是否在服务器允许的范围内,如果需要发送大量数据,可以考虑分块发送或进行优化处理。如果发送的数据包大小超过服务器端的允许范围,服务器可能会拒绝连接或关闭连接,导致连接重置。

qq_33192454的博客 4万+

3-RRR并联机器人结构详解:从运动学到奇异点分析(附MATLAB仿真代码)

本文详细解析了3-RRR并联机器人的结构设计、运动学建模及奇异点分析,并提供了MATLAB仿真代码。通过对称性设计和可分析性,3-RRR结构成为教学与研究的理想对象。文章从基础杆件参数设置到奇异位形的实时捕捉,深入探讨了机构运动的数学本质,并分享了仿真调试中的实用技巧,助力工业自动化和精密控制应用。

sunny的博客 366

扩散模型中的UNET

一、为什么UNET模型可以用于去噪网络 二、扩散模型中的UNET是一个条件去噪网络,怎么实现的 三、UNET用于分割和用去去噪的区别

m0_37737957的博客 3817

解决finalshell,连接主机报错 Session.connect: java.net.SocketException: Connection reset

解决finalshell,连接主机报错 Session.connect: java.net.SocketException: Connection reset

weixin_63782093的博客 5896

threejs中使用Reflector实现出倒影效果,并实现背景透明的效果(Reflector镜面背景透明, 倒影弱化)

在项目中遇到要实现倒影效果,threejs给我们提供了Reflector,可以轻松完成镜面效果,实现倒影。但背景不透明;并且反射出的倒影太亮了;网上没搜到解决方案, 自己修改了下源码实现了想要的效果,做个记录...

weixin_40856652的博客 5721

java.net.SocketException: Connection reset 解决方法

代码块: if (!socket.isClosed()) { int len = is.read(data); if (len == -1) {//当读取长度为 -1 时,则TCP以断开连接 LogManager.getInstance().printError(...

爱上网的大花猫 5万+

【微电网优化】基于粒子群求解CHP机组、气网、电网、储热罐和电锅炉微电网优化模型附matlab代码

1 简介 我国风力发电近年发展迅速,装机容量已成为全球首位.随着风力发电在电力系统中的渗透率不断提高,我国多个地区(尤其是"三北"地区)已经面临着十分严峻的风电消纳问题,产生不容忽视的弃风问题.针对目前我国风电消纳存在的困难,本文提出粒子群优化CHP机组、气网、电网、储热罐和电锅炉微电网控制策略. 2 部分代码 Ppv=[0;0;0;0;0;0;9.86000000000000;19.0100000000000;33.1000000000000;45.0700000000000;64.7900000

qq_59747472的博客 672

别再用第三方黑盒库了!从零手写C#+YOLOv12推理全流程,框位准、可定制、调试快10倍

从零手写YOLO推理框架,不是为了造轮子,而是为了完全掌控代码,工业现场的需求千变万化,第三方黑盒库根本没法满足所有需求。这套代码我已经在东莞长安的3家PCB厂、深圳南山的2家3C电子厂落地了,稳定运行6个月,没有出过任何问题。如果有什么问题或者需要扩展的功能,都可以在评论区交流。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 326

java.net.SocketException: Connection reset问题解决总结

解决办法:   1、网络编程时未正确捕获java.net.SocketException, 客户端先关闭就会报这个异常;(检查发送数据是否符合服务器接收格式,不然的话服务器会自动断开连接,这样,socket就会被关闭,那么你就不能使用IO流读取数据,这样就会报这个错误)   2、数据库和应用服务器的网段不同(这个查看一下,可能性较小)  3、好像说防火墙的比较多。。。。(试一下,关闭)

趴趴熊的博客 8万+

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

JAVA网络异常的小总结

第1个异常是java.net.BindException:Address already in use: JVM_Bind。该异常发生在服务器端进行new ServerSocket(port)(port是一个0,65536的整型值)操作时。异常的原因是以为与port一样的一个端口已经被启动,并进行监听。此时用netstat –an命令,可以看到一个Listending状态的端口。只需要找一个没有被

ftlovexg的专栏 1350

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

FinalShell Session.connect: java.net.ConnectException: Connection reset

处理方式,重启sshd 重启 systemctl restart sshd 正常连接

杨义权的博客 9678

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Connection reset原因分析和解决方案

为什么80%的码农都做不了架构师?>>> ...

weixin_34384557的博客 1万+

Socket java.net.SocketException: Connection reset错误异常的原因及解决

目录 一、错误代码 1.1 Socket客户端代码 1.2 Socket服务器代码 二、错误原因 三、解决方案 一、错误代码 1.1 Socket客户端代码 public class Client2 { public static void main(String[] args) { try { Socket socket =...

LaoYe的博客 2万+

com.jcraft.jsch.JSchException: Session.connect: java.net.SocketTimeoutException: Read timed out解决办法

public class JSchUtil {     public static void main(String[] args) {         initConnect("192.168.102.132", "root", "root", 22, 30000);     }          /**      * 初始化连接linux服务器      * @param host     ...

qingcyb的博客 1万+
上一篇: 为什么java重写equals的时候要重写hashCode?
下一篇: Redisson lua脚本解读
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值