Java中ch.ethz.ssh2.Connection对象的使用

       在项目中,后端开发中有时候需要获得一些远程服务器的数据进行业务处理,遇到这种情况,很容易联想到和数据库类似,通过连接对象远程连接服务器,通过调用服务器上的Shell脚本或执行命令来获取我们所需要的数据,我在这里使用了ch.ethz.ssh2.Connection类,特此记录一下,希望能对看到这篇文章的读者有所帮助。这个网址是相应的接口文档,读者可以根据需要进行查阅。

       在使用时,首先需要先建立一个Connection对象,构造函数有如下两种情况:

//准备一个新Connection对象,然后可以使用该对象建立与指定SSH-2服务器的连接。
Connection(java.lang.String hostname)
//准备一个新Connection对象,然后可以使用该对象建立与指定SSH-2服务器的连接
Connection(java.lang.String hostname, int port)      

        然后通过调用Connection对象的connect()方法进行连接,接着需要通过authenticateWithPassword()方法进行用户名和密码验证,这样基本完成与服务器的连接。执行具体的命令时通过开启Session来实现的,通过调用Connection对象的openSession()方法来获取一个Session对象,通过调用Session对象的execCommand()方法执行具体的命令,需要注意的是一个Session对象只能远程执行一条语句,所以如果读者要执行多条命令,有两种选择,一种是将命令组合成一条命令,执行一次,另一种方法是执行一条命令后就关闭Session对象,但是不要关闭Connection对象,继续开启一个新的Session对象来执行下一条命令,依次类推,执行完所有的命令,使用后记得关闭Connection对象。由于上面附有有文档的接口,所以只是阐述的调用的方法,没有说明每个方法所需要的参数,读者可根据下面的代码案例进行理解,在使用时还是建议根据文档接口选择适合自己的方法:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
public class SSHTest{
	public static void main(String[] args) {
		String hostname = "192.168.192.128";
		String username = "root";
		String password = "root";
 
		try {
			Connection conn = new Connection(hostname);
			conn.connect();
            //进行身份认证
			boolean isAuthenticated = conn.authenticateWithPassword(
					username,password);
			if (isAuthenticated == false)
				throw new IOException("Authentication failed.");
            //开启一个Session
			Session sess = conn.openSession();
            //执行具体命令
			sess.execCommand("cat haha.txt");
            //获取返回输出
			InputStream stdout = new StreamGobbler(sess.getStdout());
            //返回错误输出
			InputStream stderr = new StreamGobbler(sess.getStderr());
			BufferedReader stdoutReader = new BufferedReader(
					new InputStreamReader(stdout));
			BufferedReader stderrReader = new BufferedReader(
					new InputStreamReader(stderr));
			
			System.out.println("Here is the output from stdout:");
			while (true) {
				String line = stdoutReader.readLine();
				if (line == null)
					break;
				System.out.println(line);
			}
 
			System.out.println("Here is the output from stderr:");
			while (true) {
				String line = stderrReader.readLine();
				if (line == null)
					break;
				System.out.println(line);
			}
            //关闭Session
			sess.close();
            //关闭Connection
			conn.close();
		} catch (IOException e) {
			e.printStackTrace(System.err);
			System.exit(2);
		}
	}
}

       Connection对象进行远程调用的基本操作方法大致就是这些,接下来说一下我在使用过程中遇到的坑。

       身份认证问题。在调用connect方法后需要进行身份认证,但是如果同一个用户名和密码如果已经认证,你在继续认证会抛一个身份重复认证的异常 我们在接口文档中不难发现,我们可以通过调用isAuthenticationComplete()来验证当前Connection对象的身份认证是否可用,因为我们其实不用关注验证的是哪个账号,如果进行了身份认证,那么就可以开启Session进行远程调用,如果没有进行身份验证,则我们重新调用authenticateWithPassword()方法进行验证。还有一个要注意的地方,如果在远程调用时使用了多线程,则在判断Connection对象是否进行身份认证时(isAuthenticationComplete())时添加锁,我使用的是this锁,这样防止多线程时判断出现问题。如代码所示:

public Boolean login() {
        boolean flg = false;
        try {
            conn = new Connection(ip);
            conn.connect();// 连接
            //判断身份是否已经认证
            if (!conn.isAuthenticationComplete()) {
                //加锁,防止多线程调用时线程间判断不一致,导致出现重复认证
                synchronized (this) {
                    if (!conn.isAuthenticationComplete()) {
                        //进行身份认证
                        flg = conn.authenticateWithPassword(userName, userPwd);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
        return flg;
    }

       另外一个问题就是如果你开启定时器定时去取服务器上的数据,则会在一定的时间间隔就会执行一次远程调用,使用完毕后记得要关闭Session对象,否则隔一段时间会报java.net.SocketException: Too many open files异常,在linux服务器中,我们对服务器的操作在服务器中是以File的形式存在,在linux服务器中,对file的同时打开数目是有规定的,可以通过ulimit -a命令中返回的open files 进行查看。所以如果你一直openSession,但是不关闭,linux的file数目就会一直累增,每开启一个Session就多一个file,所以在开启定时器后,总会有一个时刻会报上面的异常,所以切记要记得在使用完毕后关闭Session。

      另外,多线程调用ch.ethz.ssh2.Connection执行shell任务时如有的执行丢失情况,可能是因为java里面线程池同时间执行的最大线程数大于liunx链接ssh最大数导致的,读者遇到时可以查看一下是不是因为这个原因,linux修改最大连接数在/etc/ssh/sshd_config中修改MaxStartups参数。如果在连接主机时遇到Authentication method password not supported by the server at this stage错误,可能是因为默认的SUSE系统配置是不允许在程序中进行远程登陆认证的,需在/etc/ssh/sshd_config中将PasswordAuthentication配置项改成yes即可。

       我建议在使用Connection进行远程执行linux命令时,可以自定义一个类,将各个功能封装,便于自己使用(我在项目中就是这样使用的,如上面的login方法),这样可以对在调用执行命令方法后可以释放掉Session等细节进行封装,方便自己使用。另外,由于Connectio对象始终不关闭,只有在最终使用完毕才关闭,所以每次开启Session前需要判断是否进行了身份验证以及身份验证是否有效,可以通过判断Connection是否为空以及与isAuthenticationComplete()方法相结合来判断,如果没有认证,则可以调用login方法进行身份认证,否则直接开启Session执行即可。

       程序之路漫漫,吾将上下而求索                

  • 18
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值