1. 程式人生 > >Telnet協議詳解及使用C# 用Socket 程式設計來實現Telnet協議

Telnet協議詳解及使用C# 用Socket 程式設計來實現Telnet協議

轉自:

http://www.cnblogs.com/jicheng1014/archive/2010/01/28/1658793.html

同步發行到atpking.com......

這因為有個任務涉及到使用telnet 來連線遠端的路由器,獲取資訊,之後進行處理.

所以需要寫一個自動telnet登入到遠端,之後獲取資訊進行處理的程式.

自己C++ 一塌糊塗,所以幾乎最開始就沒打算用C++或者C寫

論自己的實力,還是走C#路線稍微穩妥一點吧,

因為telnet 是使用tcp/ip 協議折騰的事情

很容易的想到使用socket來實現telnet

(當然你可以在程序裡啟用telnet 命令,只不過總覺得那樣不夠技術,

而且操作不自由--受限於telnet 這個指令)

ok,翻協議,弄清原理,結果比預想的難度要大一些

定義

============================================================

Telnet協議是TCP/IP協議族中應用最廣泛的協議。

它允許使用者(Telnet客戶端)通過一個協商過程來與一個遠端裝置進行通訊。

Telnet協議是基於網路虛擬終端NVT(Network Virtual Termina1)的實現,

NVT是虛擬裝置,連線雙方(客戶機和伺服器)都必須把它們的物理終端和NVT進行相互轉換

============================================================

大概意思就是   跟遠端通訊的一套協議,之後這個協議無視你機器是啥型號,啥樣子

只要是用telnet的,統統都可以看成是NVT  

(類似面向物件中的繼承關係:NVT是父類,各種實用telnet 的都繼承與NVT)

好處非常明顯,可以無視型號而直接使用標準命令,任何服從NVT 的裝置都能通訊

當然不可避免的,標準也同時代表著效能的損失:

由於NVT 得顧及到所有的各種型號的機器,所以他定義的 操作十分有限

(因為考慮到包括要支援類似9城小霸王那些效能很差,系統簡單的機器),

為了解決NVT這個"為了照顧小霸王,而導致高階裝置的功能不能用"的這個弊病,

Telnet琢磨出了一個比較好的解決方案

"用於擴充套件基本NVT功能的協議,提供了選項協商的機制"   來解決問題

類似那個經典的英國綿羊笑話

使用英文描述兩隻綿羊在路上碰到後發生的故事

=========================

綿羊A:  Hi,Sheep!

綿羊B:Hi, Can you speak Chinese?

綿羊A:yes, "jin tian chi le ma ? (今天吃了嗎?)"

綿羊B: "chi la ,hen shuang ! (吃啦,很爽!)"

.....省略500字

改卷的英國人累牛滿面,因為他不會中文,

但又不能說這篇文章有問題.

=========================

這裡英文就可以理解為NVT 的標準功能,為通用語,

而後來的中文拼音,就是擴充套件.

ok,原理就是那麼回事,講講細節吧

telnet來連線的時候,需要傳送一系列的指令來協商(綿羊協商)通訊,

流程圖類似這個

image

ok,那麼,具體的命令是怎樣的呢?

很無趣的,

就是telnet的命令格式

IAC 命令碼 選項碼

一個個的解釋.

IAC:命令解釋符,說白了就是每條指令的字首都得是它,固定值255  (11111111 B)

命令碼: 一系列定義:(最常用的250~ 254 咱加粗表示)

名稱

程式碼(十進位制)

描述

EOF

236

檔案結束符

SUSP

237

掛起當前程序(作業控制)

ABORT

238

異常中止程序

EOR

239

記錄結束符i

SE

240

自選項結束

NOP

241

無操作

DM

242

資料標記

BRK

243

中斷

IP

244

中斷程序

AO

245

異常中止輸出

AYT

246

對方是否還在執行?

EC

247

轉義字元

EL

248

刪除行

GA

249

繼續進行

SB

250

子選項開始

WILL

251

同意啟動(enable)選項

WONT

252

拒絕啟動選項

DO

253

認可選項請求

DONT

254

拒絕選項請求

選項協商:4種請求

    1)WILL:傳送方本身將啟用選項

    2)DO:傳送方想叫接受端啟用選項

    3)WONT:傳送方本身想禁止選項

    4)DONT:傳送方想讓接受端去禁止選項

緊接著就是選項碼

選項標識

名稱

1

回顯

3

抑制繼續進行

5

狀態

6

定時標記

24

終端型別

31

視窗大小

32

終端速度

33

遠端流量控制

34

行方式

36

環境變數

ok,為了搞掂這個telnet 連結,

我特地裝了個linux 作為telnet的連結物件進行telnet遠端登入

之後寫了一個噁心的程式碼來幫助我進行除錯

額,在寫這個程式之前 ,我搜了將近1天時間的網路

發現大多數程式碼註釋的不是太和諧,讀起來很難理解

所以自己根據網上的一個win程式改出了一個console 的程式

同時,我特地花了兩天時間,幾乎把每一句能寫註釋的都寫了,

基本上可以說是我目前註釋寫的最多的一次程式碼了,

程式碼特別龐大,就做個視窗放上去了

using
 System;
using
 System.Collections.Generic;
using
 System.Text;
using
 System.Net;
using
 System.Net.Sockets;
using
 System.Collections;
namespace
 ConsoleApplication1
{
    public
 class
 Program
    {
        #region
 一些telnet的資料定義,先沒看懂沒關係
        /// <summary>
        /// 標誌符,代表是一個TELNET 指令
        /// </summary>
        readonly
 Char IAC = Convert.ToChar(255);
        /// <summary>
        /// 表示一方要求另一方使用,或者確認你希望另一方使用指定的選項。
        /// </summary>
        readonly
 Char DO = Convert.ToChar(253);
        /// <summary>
        /// 表示一方要求另一方停止使用,或者確認你不再希望另一方使用指定的選項。
        /// </summary>
        readonly
 Char DONT = Convert.ToChar(254);
        /// <summary>
        /// 表示希望開始使用或者確認所使用的是指定的選項。
        /// </summary>
        readonly
 Char WILL = Convert.ToChar(251);
        /// <summary>
        /// 表示拒絕使用或者繼續使用指定的選項。
        /// </summary>
        readonly
 Char WONT = Convert.ToChar(252);
        /// <summary>
        /// 表示後面所跟的是對需要的選項的子談判
        /// </summary>
        readonly
 Char SB = Convert.ToChar(250);
        /// <summary>
        /// 子談判引數的結束
        /// </summary>
        readonly
 Char SE = Convert.ToChar(240);
        const
 Char IS = '0'
;
        const
 Char SEND = '1'
;
        const
 Char INFO = '2'
;
        const
 Char VAR = '0'
;
        const
 Char VALUE = '1'
;
        const
 Char ESC = '2'
;
        const
 Char USERVAR = '3'
;
        /// <summary>
        /// 流
        /// </summary>
        byte
[] m_byBuff = new
 byte
[100000];
        /// <summary>
        /// 收到的控制資訊
        /// </summary>
        private
 ArrayList m_ListOptions = new
 ArrayList();
        /// <summary>
        /// 儲存準備傳送的資訊
        /// </summary>
        string
 m_strResp;
        /// <summary>
        /// 一個Socket套接字
        /// </summary>
        private
 Socket s;
        #endregion
        /// <summary>
        /// 主函式
        /// </summary>
        /// <param name="args"></param>
        static
 void
 Main(string
[] args)
        {
            //例項化這個物件
            Program p = new
 Program();
            //啟動socket進行telnet 連結
            p.doSocket();
        }
        /// <summary>
        /// 啟動socket 進行telnet操作
        /// </summary>
        private
 void
 doSocket()
        {
            //獲得連結的地址,可以是網址也可以是IP
            Console.WriteLine("Server Address:"
);
            //解析輸入,如果是一個網址,則解析成ip
            IPAddress import = GetIP(Console.ReadLine());
            //獲得埠號
            Console.WriteLine("Server Port:"
);
            int
 port = int
.Parse(Console.ReadLine());
            //建立一個socket物件,使用IPV4,使用流進行連線,使用tcp/ip 協議
            s = new
 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //獲得一個連結地址物件(由IP地址和埠號構成)
            IPEndPoint address = new
 IPEndPoint(import, port);
            /*
             * 說明此socket不是處於阻止模式
             * 
             * msdn 對阻止模式的解釋:
             * ============================================================
             * 如果當前處於阻止模式,並且進行了一個並不立即完成的方法呼叫,
             * 則應用程式將阻止執行,直到請求的操作完成後才解除阻止。
             * 如果希望在請求的操作尚未完成的情況下也可以繼續執行,
             * 請將 Blocking 屬性更改為 false。Blocking 屬性對非同步方法無效。
             * 如果當前正在非同步傳送和接收資料,並希望阻止執行,
             * 請使用 ManualResetEvent 類。
             * ============================================================
            */
            s.Blocking = false
;
            /*
             * 開始一個對遠端主機連線的非同步請求,
             * 因為Telnet 使用的是TCP 連結,是面向連線的,
             * 所以此處BeginConnect 會啟動一個非同步請求,
             * 請求獲得與 給的address 的連線
             *
             * 此方法的第二個函式是一個型別為AsyncCallback 的委託
             * 
             * 這個AsyncCallback msdn給出的定義如下
             * ===================================================================
             * 使用 AsyncCallback 委託在一個單獨的執行緒中處理非同步操作的結果。A
             * syncCallback 委託表示在非同步操作完成時呼叫的回撥方法。
             * 回撥方法採用 IAsyncResult 引數,該引數隨後可用來獲取非同步操作的結果。
             * ===================================================================
             * 這個方法裡的委託實際上就是 當非同步請求有迴應了之後,執行委託的方法.
             * 委託裡的引數,實際上就是BeginConnect的第三個引數,
             * 此處為socket 本身
             * 
             * 我比較懶,寫了一個匿名委託,實際上跟AsyncCallback 效果一個樣.
             * 
             */
            s.BeginConnect(
                address,
                delegate
(IAsyncResult ar)
                /*
                 * 此處為一個匿名委託,
                 * 實際上等於 
                 * 建立一個AsyncCallback物件,指定後在此引用一個道理
                 * 
                 * ok這裡的意義是,
                 * 當遠端主機連線的非同步請求有響應的時候,執行以下語句
                 */
                {
                    try
                    {
                        //獲得傳入的物件 (此處物件是BeginConnect 的第三個引數)
                        Socket sock1 = (Socket)ar.AsyncState;
                        /*
                         * 如果 Socket 在最近操作時連線到遠端資源,則為 true;否則為 false。
                         * 
                         * 以下是MSDN 對Connected屬性的備註資訊
                         * =========================================================================
                         * Connected 屬性獲取截止到最後的 I/O 操作時 Socket 的連線狀態。
                         * 當它返回 false 時,表明 Socket 要麼從未連線,要麼已斷開連線。
                         * 
                         * Connected 屬性的值反映最近操作時的連線狀態。如果您需要確定連線的當前狀態,
                         * 請進行非阻止、零位元組的 Send 呼叫。
                         * 如果該呼叫成功返回或引發 WAEWOULDBLOCK 錯誤程式碼 (10035),
                         * 則該套接字仍然處於連線狀態;否則,該套接字不再處於連線狀態。
                         * =========================================================================