//調用方法如下:
Telnet p = new Telnet("192.168.1.100", 23, 50);
if(p.Connect()==false)
{
Console.WriteLine("連接配接失敗");
return;
}
//等待指定字元傳回後才執行下一指令
p.WaitFor("login:");
p.Send("admin");
p.WaitFor("password:");
p.Send("123456");
p.WaitFor(">");
//Console.WriteLine(p.SessionLog);
Console.WriteLine(p.WorkingData);
改進後代碼(注意标藍色的部分):
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
namespace Lwolf
public class Telnet
{
#region telnet的資料定義
/// <summary>
/// 标志符,代表是一個TELNET 指令
/// </summary>
readonly Char IAC = Convert.ToChar(255);
/// 表示一方要求另一方使用,或者确認你希望另一方使用指定的選項。
readonly Char DO = Convert.ToChar(253);
/// <summary>
/// 表示一方要求另一方停止使用,或者确認你不再希望另一方使用指定的選項。
/// </summary>
readonly Char DONT = Convert.ToChar(254);
/// 表示希望開始使用或者确認所使用的是指定的選項。
/// </summary>
readonly Char WILL = Convert.ToChar(251);
/// 表示拒絕使用或者繼續使用指定的選項。
readonly Char WONT = Convert.ToChar(252);
/// 表示後面所跟的是對需要的選項的子談判
readonly Char SB = Convert.ToChar(250);
/// 子談判參數的結束
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>
byte[] m_byBuff = new byte[100000];
/// 收到的控制資訊
private ArrayList m_ListOptions = new ArrayList();
/// 存儲準備發送的資訊
string m_strResp;
/// 一個Socket套接字
private Socket s;
#endregion
private IPEndPoint iep;
private string address;
private int port;
private int timeout;
private string strWorkingData = ""; // 儲存從伺服器端接收到的資料
private string strFullLog = "";
//==== 夏春濤 擴充 20110531 ================================================
private string strWorkingDataX = "";
//用于擷取目前工作的資料内容
public string WorkingData
{
get { return strWorkingDataX; }
}
//===================================================================
public Telnet(string Address, int Port, int CommandTimeout)
address = Address;
port = Port;
timeout = CommandTimeout;
/// 啟動socket 進行telnet操作
public bool Connect()
IPAddress import = GetIP(address);
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
iep = new IPEndPoint(import, port);
try
{
// Try a blocking connection to the server
s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect(iep);
//異步回調
AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
s.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, s);
return true;
}
catch (Exception)
return false;
/// 當接收完成後,執行的方法(供委托使用)
/// </summary>
/// <param name="ar"></param>
private void OnRecievedData(IAsyncResult ar)
//從參數中獲得給的socket 對象
Socket sock = (Socket)ar.AsyncState;
//EndReceive方法為結束挂起的異步讀取
int nBytesRec = sock.EndReceive(ar);
//如果有接收到資料的話
if (nBytesRec > 0)
{
//聲明一個字元串,用來存儲解析過的字元串
string m_strLine = "";
//周遊Socket接收到的字元
/*
* 此循環用來調整linux 和 windows在換行上标記的差別
* 最後将調整好的字元賦予給 m_strLine
*/
for (int i = 0; i < nBytesRec; i++)
{
Char ch = Convert.ToChar(m_byBuff[i]);
switch (ch)
{
case '\r':
m_strLine += Convert.ToString("\r\n");
break;
case '\n':
default:
m_strLine += Convert.ToString(ch);
}
}
try
//獲得轉義後的字元串的長度
int strLinelen = m_strLine.Length;
//如果長度為零
if (strLinelen == 0)
//則傳回"\r\n" 即回車換行
m_strLine = Convert.ToString("\r\n");
//建立一個流,把接收的資訊(轉換後的)存進 mToProcess 中
Byte[] mToProcess = new Byte[strLinelen];
for (int i = 0; i < strLinelen; i++)
mToProcess[i] = Convert.ToByte(m_strLine[i]);
//對接收的資訊進行處理,包括對傳輸過來的資訊的參數的存取和
string mOutText = ProcessOptions(mToProcess);
//==== 夏春濤 擴充 20110531 ==============================================
mOutText = ConvertToGB2312(mOutText);
strWorkingDataX = mOutText;
//===================================================================
//解析指令後傳回 顯示資訊(即除掉了控制資訊)
if (mOutText != "")
//Console.Write(mOutText);//顯示輸出//夏春濤 去掉 20110531/////////////////////////
strWorkingData = mOutText;
strFullLog += mOutText;
//接收完資料,處理完字元串資料等一系列事物之後,開始回發資料
RespondToOptions();
catch (Exception ex)
throw new Exception("接收資料的時候出錯了! " + ex.Message);
}
else// 如果沒有接收到任何資料的話
// 關閉連接配接
// 關閉socket
sock.Shutdown(SocketShutdown.Both);
sock.Close();
catch { }
/// 發送資料的函數
private void RespondToOptions()
//聲明一個字元串,來存儲 接收到的參數
string strOption;
/*
* 此處的控制資訊參數,是之前接受到資訊之後儲存的
* 例如 255 253 23 等等
*/
for (int i = 0; i < m_ListOptions.Count; i++)
//獲得一個控制資訊參數
strOption = (string)m_ListOptions[i];
//根據這個參數,進行處理
ArrangeReply(strOption);
DispatchMessage(m_strResp);
m_strResp = "";
m_ListOptions.Clear();
catch (Exception ers)
Console.WriteLine("出錯了,在回發資料的時候 " + ers.Message);
/// 解析接收的資料,生成最終使用者看到的有效文字,同時将附帶的參數存儲起來
///</summary>
///<param name="m_strLineToProcess">收到的處理後的資料</param>
/// <returns></returns>
private string ProcessOptions(byte[] m_strLineToProcess)
string m_DISPLAYTEXT = "";
string m_strTemp = "";
string m_strOption = "";
string m_strNormalText = "";
bool bScanDone = false;
int ndx = 0;
int ldx = 0;
char ch;
//把資料從byte[] 轉化成string
for (int i = 0; i < m_strLineToProcess.Length; i++)
Char ss = Convert.ToChar(m_strLineToProcess[i]);
m_strTemp = m_strTemp + Convert.ToString(ss);
//此處意義為,當沒描完資料前,執行掃描
while (bScanDone != true)
//獲得長度
int lensmk = m_strTemp.Length;
//之後開始分析指令,因為每條指令為255 開頭,故可以用此來區分出每條指令
ndx = m_strTemp.IndexOf(Convert.ToString(IAC));
//此處為出錯判斷,本無其他含義
if (ndx > lensmk)
ndx = m_strTemp.Length;
//此處為,如果搜尋到IAC标記的telnet 指令,則執行以下步驟
if (ndx != -1)
#region 如果存在IAC标志位
// 将 标志位IAC 的字元 指派給最終顯示文字
m_DISPLAYTEXT += m_strTemp.Substring(0, ndx);
// 此處獲得指令碼
ch = m_strTemp[ndx + 1];
//如果指令碼是253(DO) 254(DONT) 521(WILL) 252(WONT) 的情況下
if (ch == DO || ch == DONT || ch == WILL || ch == WONT)
//将以IAC 開頭3個字元組成的整個指令存儲起來
m_strOption = m_strTemp.Substring(ndx, 3);
m_ListOptions.Add(m_strOption);
// 将 标志位IAC 的字元 指派給最終顯示文字
m_DISPLAYTEXT += m_strTemp.Substring(0, ndx);
//将處理過的字元串删去
string txt = m_strTemp.Substring(ndx + 3);
m_strTemp = txt;
//如果IAC後面又跟了個IAC (255)
else if (ch == IAC)
//則顯示從輸入的字元串頭開始,到之前的IAC 結束
m_DISPLAYTEXT = m_strTemp.Substring(0, ndx);
//之後将處理過的字元串排除出去
m_strTemp = m_strTemp.Substring(ndx + 1);
//如果IAC後面跟的是SB(250)
else if (ch == SB)
ldx = m_strTemp.IndexOf(Convert.ToString(SE));
m_strOption = m_strTemp.Substring(ndx, ldx);
m_strTemp = m_strTemp.Substring(ldx);
#endregion
//若字元串裡已經沒有IAC标志位了
else
//顯示資訊累加上m_strTemp存儲的字段
m_DISPLAYTEXT = m_DISPLAYTEXT + m_strTemp;
bScanDone = true;
//輸出人看到的資訊
m_strNormalText = m_DISPLAYTEXT;
catch (Exception eP)
throw new Exception("解析傳入的字元串錯誤:" + eP.Message);
return m_strNormalText;
/// <summary>
/// 獲得IP位址
/// <param name="import"></param>
/// <returns></returns>
private static IPAddress GetIP(string import)
IPHostEntry IPHost = Dns.GetHostEntry(import);
return IPHost.AddressList[0];
#region magic Function
//解析傳過來的參數,生成回發的資料到m_strResp
private void ArrangeReply(string strOption)
Char Verb;
Char Option;
Char Modifier;
Char ch;
bool bDefined = false;
//排錯選項,無啥意義
if (strOption.Length < 3) return;
//獲得指令碼
Verb = strOption[1];
//獲得選項碼
Option = strOption[2];
//如果選項碼為 回顯(1) 或者是抑制繼續進行(3)
if (Option == 1 || Option == 3)
bDefined = true;
// 設定回發消息,首先為标志位255
m_strResp += IAC;
//如果選項碼為 回顯(1) 或者是抑制繼續進行(3) ==true
if (bDefined == true)
#region 繼續判斷
//如果指令碼為253 (DO)
if (Verb == DO)
//我設定我應答的指令碼為 251(WILL) 即為支援 回顯或抑制繼續進行
ch = WILL;
m_strResp += ch;
m_strResp += Option;
//如果指令碼為 254(DONT)
if (Verb == DONT)
//我設定我應答的指令碼為 252(WONT) 即為我也會"拒絕啟動" 回顯或抑制繼續進行
ch = WONT;
//如果指令碼為251(WILL)
if (Verb == WILL)
//我設定我應答的指令碼為 253(DO) 即為我認可你使用回顯或抑制繼續進行
ch = DO;
//break;
//如果接受到的指令碼為251(WONT)
if (Verb == WONT)
//應答 我也拒絕選項請求回顯或抑制繼續進行
ch = DONT;
//break;
//如果接受到250(sb,标志子選項開始)
if (Verb == SB)
/*
* 因為啟動了子标志位,指令長度擴充到了4位元組,
* 取最後一個标志位元組為選項碼
* 如果這個選項碼位元組為1(send)
* 則回發為 250(SB子選項開始) + 擷取的第二個位元組 + 0(is) + 255(标志位IAC) + 240(SE子選項結束)
*/
Modifier = strOption[3];
if (Modifier == SEND)
ch = SB;
m_strResp += ch;
m_strResp += Option;
m_strResp += IS;
m_strResp += IAC;
m_strResp += SE;
#endregion
else //如果選項碼不是1 或者3
#region 底下一系列代表,無論你發那種請求,我都不幹
catch (Exception eeeee)
throw new Exception("解析參數時出錯:" + eeeee.Message);
/// <summary>
/// 将資訊轉化成charp[] 流的形式,使用socket 進行發出
/// 發出結束之後,使用一個匿名委托,進行接收,
/// 之後這個委托裡,又有個委托,意思是接受完了之後執行OnRecieveData 方法
///
/// <param name="strText"></param>
void DispatchMessage(string strText)
//申請一個與字元串相當長度的char流
Byte[] smk = new Byte[strText.Length];
for (int i = 0; i < strText.Length; i++)
//解析字元串,将其存儲到char流中去
Byte ss = Convert.ToByte(strText[i]);
smk[i] = ss;
//發送char流,之後發送完畢後執行委托中的方法(此處為匿名委托)
IAsyncResult ar2 = s.BeginSend(smk, 0, smk.Length, SocketFlags.None, delegate(IAsyncResult ar)
//當執行完"發送資料" 這個動作後
// 擷取Socket對象,對象從beginsend 中的最後個參數上獲得
Socket sock1 = (Socket)ar.AsyncState;
if (sock1.Connected)//如果連接配接還是有效
//這裡建立一個委托
AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
sock1.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock1);
}, s);
s.EndSend(ar2);
Console.WriteLine("出錯了,在回發資料的時候:" + ers.Message);
/// 等待指定的字元串傳回
/// <param name="DataToWaitFor">等待的字元串</param>
/// <returns>傳回0</returns>
public int WaitFor(string DataToWaitFor)
long lngStart = DateTime.Now.AddSeconds(this.timeout).Ticks;
long lngCurTime = 0;
while (strWorkingData.ToLower().IndexOf(DataToWaitFor.ToLower()) == -1)
lngCurTime = DateTime.Now.Ticks;
if (lngCurTime > lngStart)
throw new Exception("Timed Out waiting for : " + DataToWaitFor);
Thread.Sleep(1);
strWorkingData = "";
return 0;
public void Send(string message)
DispatchMessage(message);
//因為每發送一行都沒有發送回車,故在此處補上
DispatchMessage("\r\n");
/// 取完整日志
public string SessionLog
get
return strFullLog;
//======================================================================================
/// 字元串編碼轉換,解決漢字顯示亂碼問題。
/// 原始字元串中的漢字存儲的是漢字内碼,此代碼實質是将漢字内碼轉換為GB2312編碼。(夏春濤20110531)
/// <param name="str_origin">需要轉換的字元串</param>
/// <returns>轉換後的字元串</returns>
private string ConvertToGB2312(string str_origin)
char[] chars = str_origin.ToCharArray();
byte[] bytes = new byte[chars.Length];
for (int i = 0; i < chars.Length; i++)
int c = (int)chars[i];
bytes[i] = (byte)c;
Encoding Encoding_GB2312 = Encoding.GetEncoding("GB2312");
string str_converted = Encoding_GB2312.GetString(bytes);
return str_converted;
}