前言
這幾天研究了一下如何在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>
這里我就不細說了:我的內容也是參考這一篇文章:請參閱
https://blog.csdn.net/moshowgame/article/details/80275084
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