背景
有些老的小型机是没有ssh登录,只有telnet登录的方式,由于监控的要求,需要登录设备执行脚本拿到数据,那么telnet稳定登录就是必不可少的一步了。
telnet登录的过程
监听XXX的意思是等待输入的数据匹配后面的正则表达式,在控制台登录时,输出telent登录时会等待系统返回指引信息,所以根据指引信息来输出账号密码,这里的输出指的是往输出流写数据,输入指的是输入流输入的数据。流程如下
commons-net介绍
apache开源的commons-net实现了一系列网络协议,包括不限于telnet,echo,ftp,nntp等,在这里我们可以利用它协助我们进行telnet登录。
使用方式,通过maven引入
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.8.0</version>
</dependency>
交互式实现登录
首先在登录前我们需要有一个组件来帮助我们管理输入输出流,并且能根据我们的需要做一些等待输入的操作,也能方便地输出。前面我已经写了一篇文章来介绍管理输入输出的组件,请参考该文章java优雅地管理输入输出流_芜湖启飞的博客-CSDN博客
具体的登录代码如下
创建连接
public HostConnection connect(Host host) throws Exception {
//termType 字体
TelnetClient telnetClient = new TelnetClient("VT220");
telnetClient.setConnectTimeout(10000);
telnetClient.setDefaultTimeout(30000);
try {
//登录第一个节点
HostConnectionNode firstHostConnectionNode = host.getNodeList().get(0);
telnetClient.connect(firstHostConnectionNode.getAddress(), firstHostConnectionNode.getPort());
} catch (Exception e) {
telnetClient.disconnect();
throw new ConnectionException("端口不通");
}
return new TelnetHostConnection(host, telnetClient);
}
此时还仅仅是建立了telnet连接,还没有进行账号密码的验证,接下来的代码就是进行账密的交互验证,,验证流程图可以往上面翻
ExchangeObserver exchangeObserver = streamExchange.getExchangeObserver();
FinishOneExchangeListener finishOneExchangeListener = new FinishOneExchangeListener();
exchangeObserver.register(finishOneExchangeListener);
String outStr;
try {
finishOneExchangeListener.refreshPattern("[L|l]ogin:");
finishOneExchangeListener.await(15, TimeUnit.SECONDS);
streamExchange.reset();
finishOneExchangeListener.refreshPattern("[P|p]assword:");
streamExchange.write(connectionNode.getUsername());
finishOneExchangeListener.await(15, TimeUnit.SECONDS);
streamExchange.reset();
finishOneExchangeListener.refreshPredict(StrUtil::isNotBlank);
streamExchange.write(connectionNode.getPassword());
finishOneExchangeListener.await(15, TimeUnit.SECONDS);
outStr = streamExchange.getPrintContent();
if (ReUtil.contains(loginFailPattern, outStr)) {
throw new ConnectionException("登录节点[%s]失败,错误信息%s".formatted(connectionNode.toString(), outStr));
}
//如果执行换行命令返回为空字符串的话则认定为登录失败
finishOneExchangeListener.refreshPredict(StrUtil::isNotBlank);
streamExchange.write(" ");
finishOneExchangeListener.await(1, TimeUnit.SECONDS);
outStr = streamExchange.getPrintContent();
//校验返回的文本来判定是否登录成功
return checkLoginMsg(connectionNode, outStr);
} catch (TimeoutException timeoutException) {
throw new ConnectionException("登录节点[%s]失败,错误信息%s".formatted(connectionNode.toString(), streamExchange.getPrintContent()));
}
以上代码都可以在开源项目中找到,指路–>shixinmuhuo/PowerExec: PowerExec是一个超强的支持无限跳板的远程执行脚本的工具。致力于解决重复繁琐的手工运维问题。 (github.com)