前言
这几天研究了一下如何在 web 页面上通过 telnet 连接一个远程的机器, 通过命令行进行控制, 让然, B/S 架构的项目, 如果直接通过
浏览器是无法和远程机器进行通信的, 我们就得借助后端来帮助实现这样的一个功能需求, 具体的实现逻辑是:
用户 ----> 控制浏览器命令行界面 ----> 发送命令到后端服务器 ----> 后端服务器连接 socket----> 推送用户的命令
telnet Server ----> 后端服务器输入流接受返回字符 ------>websocket 推送到前端完成交互
telnet
可能很多人都知道 SSH, 估计很少会了解到关于 telnet 的东西, 但是又结合起来说, 其实两个都是一种协议, 可以远程操控,
但是 telnet 在传输过程中存在着不安全的因素, 因为传输都未经过加密, 而都是明文, 这就在远程连接中, 很容易就会被抓包, 出现问题,
但 SSH 就好比是加强版的 telnet , 在传输过程中的数据进行加密, 进而减少了被盗取的风险. 并且所有的 SSH 都是经过压缩的, 在网络上的
传输速率也很优秀, 这样多方面优秀的情况下, 自然而然的就会代替掉 telnet
并不是这样说 telnet 就没什么用了, 也不是这样子的, 我们通过搭建这样一个 demo 学习的是这样的搭建过程中去了解建立连接的过程以及交互的过程
同样的, 就算改编成 SSH 其实也是这样的一个过程, 学习就是一个不断探索的过程
开始
后端的话, 从 Springboot 入手, 快速的搭建一个 Web 工程, 我们主要用这个 Web 工程来接受前端发送过来的命令请求.
包括: 连接, 断开, 发送命令等等.
最主要的, 就是采用 commons.NET 包下面的 TelnetClient
- <dependency>
- <groupId>commons.NET</groupId>
- <artifactId>commons.NET</artifactId>
- <version>3.6</version>
- </dependency>
就好比我们的交换机作为一个 Telnet 的服务端, 而我们的本机就作为一个 Telnet 的客户端, 客户端发起请求, 服务端相应, 与 B/S 其实一样的
这里我就摘一些关键的部分来进行讲解, 其他的请 clone 项目进行查看
创建一个 telnet 客户端
其实创建一个 telnet 客户端是很简单的, 就只需要 new 一个对象, 而后填写 ip 地址和端口, 端口默认是 23, 可以不写, 就打开了一个 telnet 的 Connection
- telnetClient = new TelnetClient();
- telnetClient.connect(ip,port);
- inputStream = telnetClient.getInputStream();
- outputStream = telnetClient.getOutputStream();
从客户端得到两个流, 一个输入流, 一个输出流, 这里就会涉及到 JAVA 关于 IO 相关的知识, 当然网络上一般通行都是采用字节流
inputStream 输入流 outputStread 输出流
对于输入流和输出流相对于模糊的小伙伴, 这里给大家加深一下理解, 从字面意思上理解
输入流: 就是输进来的
输出流: 就是丢出去的
其实这样理解的话, 丢失了一个基于的对象. 那就是, 到底是基于客户端还是服务端呢, 我这里简单的画个图
就好比你的程序要读取一个文件, 这个时候就需要用到输入流, 输入流提供一个 read 的方法
我得把日志信息写入到日志文件里, 这个时候就需要用到输出流, 输出流提供一个 write() 方法
输出流有一个 flush() 强制刷新的方法 强制清空和写入
一个好的习惯, 流用完之后要进行关闭!!!!!
在当前的环境里面, 输入流就好比是客户端给我们返回的数据, 而输出流才是我们推送命令的
输入命令
切记在命令后加入一个换行符, 这就好比我们在 telnet 客户端上面操作的时候, 写完一句就需要敲一个回车, 一样的道理.
通过输出流的 write 方法写入到输出流里面, flush 将输出流清空和强制写入命令.
- public String sendCommand(String send) throws IOException {
- // 加入换行符
- send = send + "\n";
- if (null == telnetClient) {
- return "连接已关闭";
- }
- outputStream.write(send.getBytes());
- outputStream.flush();
- return "发送成功";
- }
读取服务端的返回
上面已经反复叙述过了, 服务端的返回我们需要采用输入流进行接收, 同样的, 需要从流里面读取出我们需要的字节, 并将其转换为字符
通过 webSocket 推送到前台进行展示
这时候就需要起一个线程来操作了, 因为你不知道什么时候回接收到推送的消息, 所以这个线程还不能停掉, 得设置为后台线程, 让它在开启连接后一直
在后台保持活动状态, 这里就需要了解一下: 守护线程
守护线程 , 后台线程
通过给一个线程设置 setDaemon(true) 的形式标记这个线程为守护线程, 也就是后台线程, 线程启动前必须启动这个线程
对于停止这个线程的方法嘛 那就是执行中断, 在这个线程的逻辑代码中判断是否中断, 中断则跳出这个线程, 线程执行完毕
这个线程也就停掉了呗!
通过 new 一个线程类, 主要看一下这个类的实现方法
- outputThread = new InputPrintThread(inputStream);
- // 守护线程
- outputThread.setDaemon(true);
- outputThread.start();
通过一个 while 循环的方式时刻执行是否中断的方法和 对输入流进行读取, 因为读取在读取不到字节的时候会发生阻塞, 所以这样是很有必要的
因为在读取不到的时候, 会阻塞这个线程, 若读取流在文件末尾, 则会返回 - 1 我们只需要判断不是 - 1 继续读取就好了
注意: 这里说的是: 读取出一些字节, 并不是所有的字节, 一次返回过来的字节它可能会读取多次, 所以不能只循环一次就判断这个就完了
- @Override
- public void run() {
- int num = 0;
- // 字节缓冲
- char[] bytes = new char[1024];
- InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
- try {
- // 这里会发生阻塞, 通过 websocket 推送进行
- while (!interrupted() && (num = inputStreamReader.read(bytes)) != -1) {
- for (int i = 0; i <num; i++) {
- char ab = bytes[i];
- WebSocketServer.sendInfo(ab + "", SocketIdEnum.TELNET.getValue());
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
webSocket 推送
这一部分对于 Springboot 来说整合 WebSocket 简直是太简单了, 一个 POM 依赖以及简单的几行配置即可开启
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-websocket</artifactId>
- </dependency>
这里我就不细说了: 我的内容也是参考这一篇文章: 请参阅
telnet 客户端的配置
在 Windows 里面开启 telnet 基本步骤: 搜索 Windows 功能
找到并开始这一项即可, 即可在命令行里面通过 telnet + ip 进行远程访问,
telnet 测试服务端
很多小伙伴在昨晚后, 不知如何去测试 telnet 的连通性以及代码的测试, 缺少一个测试的环境
手头又没有其他闲置的电脑或者是交换机, 可以参考手头建立一个虚拟机的方式, 这样也行, 或者就
采用华为的 ENSP 创建一个虚拟的交换机后, 通过自己电脑的虚拟网卡接入到虚拟交换机, 这样其实也是可以通过 telnet
进行访问的, ENSP 的安装这里略过, 这里给一个链接: https://share.weiyun.com/5kmSEYr
而后新建一个交换机通过虚拟网卡的映射进行连接后, 通过本机 PING 这台交换机的 IP 若 ping 通则证明连接正常
若不清楚配置交换机的 telnet 可以参阅: https://www.cnblogs.com/pipci/p/8075686.html
划分 vlan 这些基础我就不细说了, 自行参考
测试效果
对与一个写后台的人来说: 前台这样子其实已经很不错了
哈哈 只能简单的写个窗口, 然后将后台推送的内容 push 到页面上
因为是一个小的测试 demo 有兴趣的可以进行改造升级更好的 CSS 美化
小结
关于研究这个 Web 版本的 telnet 其实还是涉及到很多东西, 比如又再一次的学习到了流的概念以及
之前从未知道这个输入流还会存在阻塞的问题 以及如何优雅的关闭流以及 flush 强制刷新写入等诸多的问题
虽然 SSH 已经是远程当中的主流, telnet 也未必没得用, 总结一下总是好的.
参考
交换机 telnet 配置 https://www.cnblogs.com/pipci/p/8075686.html
springboot 配置 websocket https://www.cnblogs.com/pipci/p/8075686.html
代码开源
https://gitee.com/mrc1999/web-telnet
来源: https://www.cnblogs.com/ChromeT/p/12112593.html