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來連線的時候,需要傳送一系列的指令來協商(綿羊協商)通訊,
流程圖類似這個
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),
* 則該套接字仍然處於連線狀態;否則,該套接字不再處於連線狀態。
* =========================================================================