590 likes | 712 Views
課程參與度之評估方式. 上課時必須專心聽講,跟上進度,參與討論 扣分項目 玩線上遊戲一次扣 1 分 玩手機一次扣 1 分 睡覺一次扣 1 分 聊天一次扣 1 分 無法回答老師提出的問題一次扣 1 分 加分項目 主動回答老師的問題一次加 2 分 找出老師程式中的錯誤一次加 1 分 修正老師程式中的錯誤一次加 4 分. 網路程式設計 第八章 TELNET 通訊協定. 鄧姚文. 大綱. TELNET RFC 客戶端初階實作 實作終端機模式選項 物件導向方式設計 TelnetClient 類別. 前言. TELNET 通訊協定
E N D
課程參與度之評估方式 • 上課時必須專心聽講,跟上進度,參與討論 • 扣分項目 • 玩線上遊戲一次扣1分 • 玩手機一次扣1分 • 睡覺一次扣1分 • 聊天一次扣1分 • 無法回答老師提出的問題一次扣1分 • 加分項目 • 主動回答老師的問題一次加2分 • 找出老師程式中的錯誤一次加1分 • 修正老師程式中的錯誤一次加4分
大綱 • TELNETRFC • 客戶端初階實作 • 實作終端機模式選項 • 物件導向方式設計 • TelnetClient類別
前言 • TELNET通訊協定 • 屬於TCP/IP中的TCP通訊協定之一 • 目的是達成遠端登入功能 • 使用者可以利用網路連結至伺服主機 • 進行遠端主機上的操作 • 另一個應用是BBS
TELNET RFC 定義伺服端與客戶端在網路上傳輸文字資料的方法 完成整個通訊協定必需要有Telnet Server與Telnet Client兩個分別都實作TELNET通訊協定的軟體 RFC是由IETF(The Internet Engineering Task Force)維護 網址 http://www.ietf.org
TELNET RFC • NVT虛擬網路終端機 • 想要讓異質的兩端能相互「溝通」,就必需要有一個「虛擬的中介系統」 • 能在異質系統之間達到一致性的原則,使其能夠相互溝通
TELNET RFC • IAC(Interpret As Command) • 當客戶端或伺服端要傳送控制字元時,必須先在控制字元前加上特殊的辦認字元「IAC(Interpret As Command)」 • 定義為十進位255 • 當兩端收到IAC時,代表後續的字元是控制字元 • 除了控制字元以外的都是屬於一般資料
TELNET RFC • TELNET的選項(功能) • 光靠NVT的「簡易」功能還是不夠 • TELNET中定義了一些「額外選項」,可以加強NVT的不足,例如: • 遠端所支援或使用畫面最寬是多少字元,或最高是多少字元 • TELNET制定了溝通額外選項的方法
TELNET RFC • TELNET的選項(功能) • 常用的號碼與選項對應
TELNET RFC • TELNET的選項(功能) • TELNET的溝通協議規則 • WILL(251): • 當使用於主動時,代表將進行某選項。而用於被動回應時,代表認可與確認對方請求執行的選項,並打算進行下一步的動作。 • WON'T(252): • 用來回應對方DO的請求,代表拒絕對方的請求。 • DO(253): • 當使用於主動時,代表請求對方執行某選項。而用於被動回應時,代表認同對方想要執行的選項,並等待對方進一步動作。 • DON'T(254): • 當用於主動時,代表希望對方停止已經在進行的選項功能。
TELNET RFC • TELNET的選項(功能) • 溝通範例 • 範例1 • A方希望對方(B方)傳送終端機的模式,但B方不支援此一選項時,整個協定過程應該是: • A方傳送 IAC DO Terminal-Type • B方傳送 IAC WONT Terminal-Type • 範例2 • A方希望對方(B方)傳送終端機的模式,而B方也願意進行此選項時,整個協定過程應該是: • A方傳送 IAC DO Terminal-Type • B方傳送 IAC WILL Terminal-Type
TELNET RFC • TELNET的選項(功能) • 溝通範例 • 範例3 • A方希望對方(B方)進行「暫時停頓自由傳送資料」的選項-Suppress Go Ahead(SG)機的模式,而B方也願意支援SG選項時,整個協定過程應該是: • A方傳送 IAC WILL SG • B方傳送 IAC DO SG • 範例4 • A方希望對方(B方)進行「暫時停頓自由傳送資料」的選項-Suppress Go Ahead(SG)機的模式,而B方並不想要進行此選項時,整個協定過程應該是: • A方傳送 IAC WILL SG • B方傳送 IAC DONT SG
客戶端初階實作 將用戶端與伺服器勾選 準備測試用TELNET伺服器
客戶端初階實作 按滑鼠右鍵 按下 準備測試用TELNET伺服器
客戶端初階實作 啟動類型改自動 點擊「啟動」鈕 按下 準備測試用TELNET伺服器
客戶端初階實作 • 準備測試用TELNET伺服器 • 開啟命令提示字元進行測試,輸入telnet localhost: • 輸入命令後,會出現詢問畫面,代表伺服器正常運作,可以按下y繼續:
客戶端初階實作 • 準備測試用TELNET伺服器 • 出現登入畫面: • telnet介面結束連線必需先按下Ctrl+] ,再輸入quit指令即可回到命令提示字元:續:
客戶端初階實作 Socket s = new Socket("localhost", 23); InputStream in = s.getInputStream(); • int data = in.read(); • for (inti=0; i<15; i++){ • System.out.print(data+","); • data = in.read(); • } ▌執行結果 255,253,37,255,251,1,255,251,3,255,253,39,255,253,31, 連線與資料流 讀取資料
客戶端初階實作 • 辨認命令與資料 • TELNET協定中,最重要的知識是命令與資料的辨認方法,由本章RFC國際標準的整理內容中可以得知,TELNET協定中傳輸的位元組中,傳送的內容若是以IAC(255)開頭的,代表後續是「命令」,而命令以外的資料皆是一般資料,是要顯示在畫面中給使用者的。
客戶端初階實作 • 辨認命令與資料 • 第1組 255 253 37 (IAC DO AUTH) • 伺服器要求客戶端傳送選項37,也就是「Authentication Option」,用途是它想要與用戶端溝通有關帳號驗證的資訊。 • 第2組 255 251 1 (IAC WILL ECHO) • 伺服器希望客戶端能進行選項1,也就是「Echo」功能,用途是進行回音資訊的傳送與接收。
客戶端初階實作 • 辨認命令與資料 • 第3組 255 251 3 (IAC WILL SG) • 伺服器希望客戶端能進行選項3,也就是「Suppress Go Ahead」功能,一般來說TELNET的用戶端與伺服器端傳送與接收資料時是採用全雙工的模式,傳送與接收是同步操作,但有時候會希望轉換成半雙工模式。此一溝通是伺服器希望能與用戶端是否能禁止「Go Ahead」的傳輸。 • 第4組 255 253 39 (IAC DO NEW-ENV) • 伺服器要求客戶端傳送選項39,也就是「New Environment Option 」功能,用途是它希望用戶端能傳送環境變數。
客戶端初階實作 • 辨認命令與資料 • 第5組 255 253 31 (IAC DO NAWS) • 伺服器要求客戶端傳送選項31,也就是「Negotiate About Window Size」,用來得到客戶端的畫面寬度與高度可容納的字元數。 • 整個連線傳輸的示意圖如下:
客戶端初階實作 • 命令的回覆(回應) • 在初期設計時,可以先不實作上一小節介紹的相關功能,只解讀並回應不處理該選項即可,歸納處理命令的方法可以採用當伺服器詢問或要求(以DO或WILL)時,客戶端一律回應拒絕或不支援(以WONT或DONT),為了增加程式碼的可讀性,筆者定義了幾個常用的選項常數,對應各個選項號碼如WILL、DO、WONT、DONT與IAC:
客戶端初階實作 01 package com.ch08; 02 03 import java.io.IOException; 04 import java.io.InputStream; 05 import java.io.OutputStream; 06 import java.net.Socket; 07 import java.net.UnknownHostException; 08 09 public class SimpleTelnet { 10 public static finalint IAC = 255; 11 public static finalint WILL = 251; 12 public static finalint WONT = 252; 13 public static finalint DO = 253; 14 public static finalint DONT = 254; 15 16 public static void main(String[] args) 17 throws UnknownHostException, IOException { 18 Socket s = new Socket("localhost", 23);
客戶端初階實作 19 InputStream in = s.getInputStream(); 20 OutputStream out = s.getOutputStream(); 21 int data = in.read(); 22 for (inti = 0; i < 40; i++) { 23 if (data == IAC) { 24 int tone = in.read(); 25 if (tone == DO) { 26 int option = in.read(); 27 System.out.println(IAC + "," + tone + "," + option); 28 out.write(IAC); 29 out.write(WONT); 30 out.write(option); 31 out.flush(); 32 System.out.println(" SENT:" + IAC + "," + WONT + "," 33 + option); 34 } 35 if (tone == WILL) { 36 int option = in.read();
客戶端初階實作 37 System.out.println(IAC + "," + tone + "," + option); 38 out.write(IAC); 39 out.write(DONT); 40 out.write(option); 41 out.flush(); 42 System.out.println(" SENT:" + IAC + "," + DONT + "," 43 + option); 44 } 45 } else { 46 System.out.print((char) data); 47 } 48 data = in.read(); 49 } 50 } 51 }
客戶端初階實作 ▌程式碼解說 ‧第10至14行,預先定義常數,以名稱代替不容易瞭解的整數值,提高日後程式碼的可讀性。 ‧第16-17行,本類別的執行方法main,此方法會拋出的例外物件。 ‧第18行,產生連往本機localhost的埠號23的連線物件s。 ‧第19行,由s連線物件得到輸入資料流物件in。 ‧第20行,由s連線物件得到輸出資料流物件out。 ‧ 第21行,先讀取一個位元組,放在整數data中。 ‧第22至51行,讀取40個位元組的for迴圈。 ‧第23行至44行,若是讀取到的資料是IAC,代表後續的資料是指令,應再將後續資料讀入,進行指令的判別。 ‧第25至34行,若指令的語氣為DO,則回應WONT,代表無法辨認該選項。 ‧第35至44行,若指令的語氣為WILL,則回應DONT,代表無法進行該選項的後續要求。 ‧第45至47行,若讀取的資料非IAC時,則是一般文字,可轉型為字元char後印出。 ‧第48行,讀取下一個資料後返回for迴圈。
客戶端初階實作 • 命令的回覆(回應) • 從連線後至溝通ECHO選項的連線過程示意圖如下:
客戶端初階實作 • 命令的回覆(回應) • 接著後續由溝通SG選項至NAWS選項的連線過程示意圖如下:
客戶端初階實作 執行結果顯示出當伺服器送出命令,要求某些額外的選項時客戶端皆拒絕,經過選項37、1、3、39、31、0的命令交談後,伺服器開始在最後送出一般資料「Welcome to Microsoft Telnet」等歡迎訊息: 255,253,37 SENT:255,252,37 255,251,1 SENT:255,254,1 255,251,3 SENT:255,254,3 255,253,39 SENT:255,252,39 255,253,31 SENT:255,252,31 255,253,0 SENT:255,252,0 255,251,0 SENT:255,254,0 Welcome to Microsoft Telnet Servi
客戶端初階實作 • 命令的回覆(回應) • 最後傳輸一般資料的示意圖如下:
客戶端初階實作 ?J¡Ó??s?? BBS ?t????¡±??t??: 0.02 0.02 • 中文BIG5碼 • 因為TELNET協定是以位元組byte做為傳輸資料的單位,若是資料中含有中文Big5碼而不特別處理時,將會顯示出問號或亂碼。筆者將SimpleTelnet類別的第17行連線主機改成bbs.tku.edu.tw,執行結果將顯示亂碼如下: • 因此,筆者設計一個可以自動讀取正確Big5中文的方法getBig5Char,它是個靜態方法(static),並傳入一個位元組(目前讀到的資料)與輸入資料流,getBig5Char方法程式片段如下:
客戶端初階實作 01 public char getBig5Char(int data, InputStream in) throws 02 IOException { 03 char c = (char) data; 04 if (data > 127) { 05 byte[] big5 = new byte[2]; 06 big5[0] = (byte) data; 07 big5[1] = (byte) in.read(); 08 c = new String(big5, "BIG5").charAt(0); 09 } 10 return c; 11 } 46 System.out.print((char)data); • 中文BIG5碼 • 將getBig5Char方法加入原SimpleTelnet類別中,並修改原用來顯示資料的第43行為:
客戶端初階實作 • 46 System.out.print(getBig5Char(data, in)); 蛋捲廣場 BBS 系統平均負荷: 0.05 0.03 0.03 [系統上 • 中文BIG5碼 • 修改為: • 再次執行連線至bbs.tku.edu.tw時,便可正確處理Big5碼中文了,結果如下:
實作終端機模式選項 • 終端模式 • 終端機規格稱為「終端機模式(Terminal type)」,最通用的終端機模式為「VT100」 • 大部份的個人電腦或蘋果電腦都支援此模式,其支援度最為廣泛 • 若是要支援如ANSI終端機模式時,就要在TELNET連線初期由客戶端傳送「ANSI」終端機模式至伺服器
實作終端機模式選項 • 傳送終端機模式的標準步驟 • 由RFC1091國際標準訂定 • 選項的編號為24 • TELNET協定中定義了「子項溝通」(Option negotiation)的功能,能夠讓如終端機模式這類選項進行子選項的附屬溝通功能,以傳遞較複雜的字元資料
實作終端機模式選項 • 傳送終端機模式的標準步驟 • 子項溝通 • 要求對方傳送的一方應先以「溝通開始」(subnegotiation begin;簡寫為SB) 告知 • 並以「溝通結束SE」(subnegotiation end;以下簡寫為SE) 的規範格式送出至對方 (subnegotiation end ;以下簡寫為SE)
實作終端機模式選項 • 傳送終端機模式的標準步驟 • 子項溝通 • B方在收到A方傳送的資料後,再以子項溝通的規範格式 • 將自己的終端機模式(此以ANSI為例)字串傳送至對方
實作終端機模式選項 • 資訊的傳遞方式實作 • IAC DO TERMINAL-TYPE • 傳送者(通常是伺服器)想要接收對方的終端機模式。 • IAC WILL TERMINAL-TYPE • 傳送此指令的一方(通常是客戶端)將要傳送自己的終端機模式至對方。 • IAC SB TERMINAL-TYPE SEND IAC SE • 傳送者告知對方可以用sub-negotiation的方式將終端機模式資訊送過來。 • IAC SB TERMINAL-TYPE IS ... IAC SE • 傳送者以sub-negotiation方式,傳送終端機模式。
物件導向方式設計TelnetClient類別 • 建構子 • public TelnetClient(String host, int port) • 提供主機名稱或IP位址的參數host字串與埠號資料,即可產生物件。 • 屬性 • String host • 將要建立連線的遠端主機名稱或IP位址。 • int port • 遠端主機連線埠號。 • private InputStream in; • 連線的輸入資料流。
物件導向方式設計TelnetClient類別 • 屬性 • private OutputStream out; • 連線的輸出資料流。 • public static final int IAC = 255; • 設定一個常數名為IAC的整數值,代表TELNET協定命令辨識碼255。 • 方法 • public void connect() • 連線至遠端TELNET伺服器 • void processLoop() • 資料讀取迴圈,持續讀取伺服器傳來的資料,並進行資料與命令的辨認。
物件導向方式設計TelnetClient類別 • 方法 • private void negotiation(int option) • 處理sub-negotiation各種選項的方法,目前支援終端機模式傳送。 • private void denyOption(int tone, int option) • 依照傳入值,回應拒絕伺服器所要求的選項 • private void allowOption(int tone, int option) • 依照傳入值,回應認同伺服器所要求的選項
物件導向方式設計TelnetClient類別 01 package com.ch08; 02 03 import java.io.IOException; 04 import java.io.InputStream; 05 import java.io.OutputStream; 06 import java.net.Socket; 07 import java.net.UnknownHostException; 08 09 public class TelnetClient { 10 String host; 11 int port; 12 InputStream in; 13 OutputStream out; 14 public static finalint IAC = 255; 15 public static finalint WILL = 251; 16 public static finalint WONT = 252; 17 public static finalint DO = 253; 18 public static finalint DONT = 254;