聲明:下面的三個類都是從網上找到的,具體出處不詳,哪一個類的好壞性能優劣本身把握,可參考使用。拒絕使用商業用途,如產生版權糾紛和本人無關。linux
一:Telnet連接網絡設備,在網上看到C#Telnet鏈接網絡設備的類,程序爲命令行程序,具體代碼以下:windows
文件名:Program.cs服務器
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Net.Sockets; using System.Net; namespace ConsoleTelnet { public class TelNet { #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) { //實例化這個對象 TelNet p = new TelNet(); //啓動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 會啓動一個異步請求, (本文下載自防鏽油文檔綜合站www.hthrt.com。轉載請說明出處) * 請求得到與給的address 的鏈接 * * 此方法的第二個函數是一個類型爲AsyncCallback 的委託 * * 這個AsyncCallback msdn 給出的定義以下 * =================================================================== * 使用AsyncCallback 委託在一個單獨的線程中處理異步操做的結果。A * syncCallback 委託表示在異步操做完成時調用的回調方法。 * 回調方法採用IAsyncResult 參數,該參數隨後可用來獲取異步操做的結果。 * =================================================================== * 這個方法裏的委託實際上就是當異步請求有迴應了以後,執行委託的方法. * 委託裏的參數,實際上就是BeginConnect 的第三個參數, * 此處爲socket 自己 *(本文下載自防鏽油文檔綜合站www.hthrt.com。轉載請說明出處) * 我比較懶,寫了一個匿名委託,實際上跟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 調用。 (本文下載自防鏽油文檔綜合站www.hthrt.com。轉載請說明出處) * 若是該調用成功返回或引起WAEWOULDBLOCK 錯誤代碼(10035), * 則該套接字仍然處於鏈接狀態;不然,該套接字再也不處於鏈接狀態。 * ========================================================================= */ if (sock1.Connected) { AsyncCallback recieveData = new AsyncCallback(OnRecievedData); /* * 此處沒再用匿名委託的緣由是, * 一個匿名委託嵌套一個匿名委託,我本身思路跟不上來了... * * ok,這裏是當Connected 爲true 時, * 使用BeginReceive 方法 * 開始接收信息到m_byBuff(咱們在類中定義的私有屬性) * * 如下是MSDN 對BeginReceive 的一些說明 * ========================================================================= * 異步BeginReceive 操做必須經過調用EndReceive 方法來完成。 * 一般,該方法由callback 委託調用。此方法在操做完成前不會進入阻止狀態。* 若要 一直阻塞到操做完成時爲止,請使用Receive 方法重載中的一個。 * 若要取消掛起的BeginReceive,請調用Close 方法。* ========================================================================== * * 當接收完成以後,他們就會調用OnRecievedData 方法 * 我在recieveData 所委託的方法OnRecievedData 中調用了sock.EndReceive(ar); */ sock1.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock1); } } catch (Exception ex) { Console.WriteLine("初始化接收信息出錯:" + ex.Message); } }, s); //此處是爲了發送指令而不停的循環 while (true) { //發送讀出的數據 DispatchMessage(Console.ReadLine()); //由於每發送一行都沒有發送回車,故在此處補上 DispatchMessage("\r\n"); } } /// <summary> /// 當接收完成後,執行的方法(供委託使用) /// </summary> /// <param name="ar"></param> private void OnRecievedData(IAsyncResult ar) { //從參數中得到給的socket 對象 Socket sock = (Socket)ar.AsyncState; /* * EndReceive 方法爲結束掛起的異步讀取 * (貌似是在以前的beginReceive 收到數據以後, * socket 只是"掛起",並未結束) * 以後返回總共接收到的字流量 * * 如下是MSDN 給出的EndReceive 的注意事項 * ========================================================================================= * EndReceive 方法完成在BeginReceive 方法中啓動的異步讀取操做。 * * 在調用BeginReceive 以前,需建立一個實現AsyncCallback 委託的回調方法。 * 該回調方法在單獨的線程中執行並在BeginReceive 返回後由系統調用。 * 回調方法必須接受BeginReceive 方法所返回的IAsyncResult 做爲參數。 * * 在回調方法中,調用IAsyncResult 的AsyncState 方法以獲取傳遞給BeginReceive 方法的狀態對象。 * 從該狀態對象提取接收Socket。在獲取Socket 以後,能夠調用EndReceive 方法以成功完成讀取操做, * 並返回已讀取的字節數。 * * EndReceive 方法將一直阻止到有數據可用爲止。 * 若是您使用的是無鏈接協議,則EndReceive 將讀取傳入網絡緩衝區中第一個排隊的可用數據報。 * 若是您使用的是面向鏈接的協議,則EndReceive 方法將讀取全部可用的數據, * 直到達到BeginReceive 方法的size 參數所指定的字節數爲止。 * 若是遠程主機使用Shutdown 方法關閉了Socket 鏈接,而且全部可用數據均已收到, * 則EndReceive 方法將當即完成並返回零字節。 * * 若要獲取接收到的數據,請調用IAsyncResult 的AsyncState 方法, * 而後提取所產生的狀態對象中包含的緩衝區。 * * 若要取消掛起的BeginReceive,請調用Close 方法。 * ========================================================================================= */ int nBytesRec = sock.EndReceive(ar); //若是有接收到數據的話 if (nBytesRec > 0) { //將接收到的數據轉個碼,順便轉成string 型 string sRecieved = Encoding.GetEncoding("utf-8").GetString(m_byBuff, 0, nBytesRec); //聲明一個字符串,用來存儲解析過的字符串 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': break; default: m_strLine += Convert.ToString(ch); break; } } 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]); // Process the incoming data //對接收的信息進行處理,包括對傳輸過來的信息的參數的存取和 string mOutText = ProcessOptions(mToProcess); mOutText = ConvertToGB2312(mOutText); //解析命令後返回顯示信息(即除掉了控制信息) if (mOutText != "") Console.Write(mOutText); // Respond to any incoming commands //接收完數據,處理完字符串數據等一系列事物以後,開始回發數據 RespondToOptions(); } catch (Exception ex) { throw new Exception("接收數據的時候出錯了! " + ex.Message); } } else// 若是沒有接收到任何數據的話 { // 輸出關閉鏈接 Console.WriteLine("Disconnected", sock.RemoteEndPoint); // 關閉socket sock.Shutdown(SocketShutdown.Both); sock.Close(); Console.Write("Game Over"); Console.ReadLine(); } } /// <summary> /// 發送數據的函數 /// </summary> private void RespondToOptions() { try { //聲明一個字符串,來存儲接收到的參數 string strOption; /* * 此處的控制信息參數,是以前接受到信息以後保存的 * 例如255 253 23 等等 * 具體參數的含義須要去查telnet 協議 */ 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> /// 解析接收的數據,生成最終用戶看到的有效文字,同時將附帶的參數存儲起來 /// </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; try { //把數據從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) { m_DISPLAYTEXT = m_strTemp.Substring(0, ndx); ldx = m_strTemp.IndexOf(Convert.ToString(SE)); m_strOption = m_strTemp.Substring(ndx, ldx); m_ListOptions.Add(m_strOption); 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 地址 /// </summary> /// <param name="import"></param> /// <returns></returns> private static IPAddress GetIP(string import) { //IPHostEntry IPHost = Dns.GetHostEntry(import); //IPHostEntry IPHost = Dns.GetHostByName(import); IPAddress[] IPHost = Dns.GetHostAddresses(import); //return IPHost.AddressList[0]; return IPHost[0]; //return IPHost; } #region magic Function //解析傳過來的參數,生成回發的數據到m_strResp private void ArrangeReply(string strOption) { try { 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; m_strResp += ch; m_strResp += Option; } //若是命令碼爲251(WILL) if (Verb == WILL) { //我設置我應答的命令碼爲253(DO) 即爲我承認你使用回顯或抑制繼續進行 ch = DO; m_strResp += ch; m_strResp += Option; //break; } //若是接受到的命令碼爲251(WONT) if (Verb == WONT) { //應答我也拒絕選項請求回顯或抑制繼續進行 ch = DONT; m_strResp += ch; m_strResp += Option; // 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 底下一系列表明,不管你發那種請求,我都不幹 if (Verb == DO) { ch = WONT; m_strResp += ch; m_strResp += Option; } if (Verb == DONT) { ch = WONT; m_strResp += ch; m_strResp += Option; } if (Verb == WILL) { ch = DONT; m_strResp += ch; m_strResp += Option; } if (Verb == WONT) { ch = DONT; m_strResp += ch; m_strResp += Option; } #endregion } } catch (Exception eeeee) { throw new Exception("解析參數時出錯:" + eeeee.Message); } } /// <summary> /// 將信息轉化成charp[] 流的形式,使用socket 進行發出 /// 發出結束以後,使用一個匿名委託,進行接收, /// 以後這個委託裏,又有個委託,意思是接受完了以後執行OnRecieveData 方法 /// /// </summary> /// <param name="strText"></param> void DispatchMessage(string strText) { try { //申請一個與字符串至關長度的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 流,以後發送完畢後執行委託中的方法(此處爲匿名委託) /*MSDN 對BeginSend 的解釋 * ============================================================================================ =========== * BeginSend 方法可對在Connect、BeginConnect、Accept 或BeginAccept 方法中創建的遠程主機啓 動異步發送操做。 * 若是沒有首先調用Accept、BeginAccept、Connect 或BeginConnect,則BeginSend 將會引起異常。 * 調用BeginSend 方法將使您可以在單獨的執行線程中發送數據。 * 您能夠建立一個實現AsyncCallback 委託的回調方法並將它的名稱傳遞給BeginSend 方法。 * 爲此,您的state 參數至少必須包含用於通訊的已鏈接或默認Socket。 * 若是回調須要更多信息,則能夠建立一個小型類或結構,用於保存Socket 和其餘所需的信息。 * 經過state 參數將此類的一個實例傳遞給BeginSend 方法。 * 回調方法應調用EndSend 方法。 * 當應用程序調用BeginSend 時,系統將使用一個單獨的線程來執行指定的回調方法, * 並阻止EndSend,直到Socket 發送了請求的字節數或引起了異常爲止。 * 若是但願在調用BeginSend 方法以後使原始線程阻止,請使用WaitHandle.WaitOne 方法。 * 當須要原始線程繼續執行時,請在回調方法中調用T:System.Threading.ManualResetEvent 的Set 方 法。 * 有關編寫回調方法的其餘信息,請參見Callback 示例。 * ============================================================================================ =========== */ 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); /* * 此處爲:開始接受數據(在發送完畢以後-->出自於上面的匿名委託), * 當接收完信息以後,執行OnrecieveData 方法(由委託傳進去), * 注意,是異步調用 */ sock1.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock1); } }, s); /* * 結束異步發送 * EndSend 完成在BeginSend 中啓動的異步發送操做。 * 在調用BeginSend 以前,需建立一個實現AsyncCallback 委託的回調方法。 * 該回調方法在單獨的線程中執行並在BeginSend 返回後由系統調用。 * 回調方法必須接受BeginSend 方法所返回的IAsyncResult 做爲參數。 * * 在回調方法中,調用IAsyncResult 參數的AsyncState 方法能夠獲取發送Socket。 * 在獲取Socket 以後,則能夠調用EndSend 方法以成功完成發送操做,並返回發送的字節數。 */ s.EndSend(ar2); } catch (Exception ers) { Console.WriteLine("出錯了,在回發數據的時候:" + ers.Message); } } #endregion /// <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; } } }
二:Telnet類網絡
文件名:TelnetClass.cs異步
using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections; using System.Threading; namespace EmptyProjectNet40_FineUI.App_Code.Helper { // //調用方法以下: //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); //改進後代碼(注意標藍色的部分): public class Telnet { #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 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; } /// <summary> /// 啓動socket 進行telnet操做 /// </summary> public bool Connect() { IPAddress import = GetIP(address); //IPAddress import = 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> /// 當接收完成後,執行的方法(供委託使用) /// </summary> /// <param name="ar"></param> private void OnRecievedData(IAsyncResult ar) { try { //從參數中得到給的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': break; default: m_strLine += Convert.ToString(ch); break; } } 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 { } } /// <summary> /// 發送數據的函數 /// </summary> private void RespondToOptions() { try { //聲明一個字符串,來存儲 接收到的參數 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> /// 解析接收的數據,生成最終用戶看到的有效文字,同時將附帶的參數存儲起來 ///</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; try { //把數據從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) { m_DISPLAYTEXT = m_strTemp.Substring(0, ndx); ldx = m_strTemp.IndexOf(Convert.ToString(SE)); m_strOption = m_strTemp.Substring(ndx, ldx); m_ListOptions.Add(m_strOption); 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地址 /// </summary> /// <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) { try { 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; m_strResp += ch; m_strResp += Option; } //若是命令碼爲251(WILL) if (Verb == WILL) { //我設置我應答的命令碼爲 253(DO) 即爲我承認你使用回顯或抑制繼續進行 ch = DO; m_strResp += ch; m_strResp += Option; //break; } //若是接受到的命令碼爲251(WONT) if (Verb == WONT) { //應答 我也拒絕選項請求回顯或抑制繼續進行 ch = DONT; m_strResp += ch; m_strResp += Option; //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 底下一系列表明,不管你發那種請求,我都不幹 if (Verb == DO) { ch = WONT; m_strResp += ch; m_strResp += Option; } if (Verb == DONT) { ch = WONT; m_strResp += ch; m_strResp += Option; } if (Verb == WILL) { ch = DONT; m_strResp += ch; m_strResp += Option; } if (Verb == WONT) { ch = DONT; m_strResp += ch; m_strResp += Option; } #endregion } } catch (Exception eeeee) { throw new Exception("解析參數時出錯:" + eeeee.Message); } } /// <summary> /// 將信息轉化成charp[] 流的形式,使用socket 進行發出 /// 發出結束以後,使用一個匿名委託,進行接收, /// 以後這個委託裏,又有個委託,意思是接受完了以後執行OnRecieveData 方法 /// /// </summary> /// <param name="strText"></param> void DispatchMessage(string strText) { try { //申請一個與字符串至關長度的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); } catch (Exception ers) { Console.WriteLine("出錯了,在回發數據的時候:" + ers.Message); } } /// <summary> /// 等待指定的字符串返回 /// </summary> /// <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"); } #endregion /// <summary> /// 取完整日誌 /// </summary> public string SessionLog { get { return strFullLog; } } //====================================================================================== /// <summary> /// 字符串編碼轉換,解決漢字顯示亂碼問題。 /// 原始字符串中的漢字存儲的是漢字內碼,此代碼實質是將漢字內碼轉換爲GB2312編碼。(夏春濤20110531) /// </summary> /// <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; } //====================================================================================== } }
三:Ctelnet.cs類socket
using System; using System.Text; using System.Net.Sockets; using FineUI; /// <summary> /// Summary description for ClassTelnet /// </summary> public class ClassTelnet { TcpClient telnet_tcp_client; public string strhost; // IP 地址 public string strusername; // username public string strpassword; // password private int ilogin_wait_time = 200; //網絡延遲等待時間 private int irecv_wait_time = 100; //網絡延遲等待時間 //Telnet protocal key enum Verbs { WILL = 251, WONT = 252, DO = 253, DONT = 254, IAC = 255 } public ClassTelnet() { } /** * Telnet 關閉鏈接 */ public void close_telnet() { try { if (telnet_tcp_client == null) { return; } if (telnet_tcp_client.Connected) { telnet_tcp_client.Close(); } } catch (Exception ex) { //Consule.Write("異常"); Alert.Show("異常"); } } /** * Telnet鏈接到服務器 */ public bool open_connect() { bool blresult; string strtemp; blresult = true; try { // new socket telnet_tcp_client = new TcpClient(this.strhost, 23); System.Threading.Thread.Sleep(ilogin_wait_time); // read host info data strtemp = recv_data_from_host(); //strtemp = ConvertToGB2312(strtemp); blresult = strtemp.TrimEnd().EndsWith(":"); if (blresult == false) { Alert.Show("read host info data error"); return blresult; } // username send to host blresult = send_data_to_host(this.strusername + "/n/r"); if (blresult == false) { Alert.Show("username send error"); return blresult; } System.Threading.Thread.Sleep(ilogin_wait_time); strtemp = recv_data_from_host(); blresult = strtemp.TrimEnd().EndsWith(":"); if (blresult == false) { return blresult; } // password send to host blresult = send_data_to_host(this.strpassword + "/n/r"); if (blresult == false) { return blresult; } System.Threading.Thread.Sleep(ilogin_wait_time); strtemp = recv_data_from_host(); if ((strtemp.Trim().LastIndexOf("#") > -1) || (strtemp.Trim().LastIndexOf("$") > -1) || (strtemp.Trim().LastIndexOf(">") > -1)) { blresult = true; } else { blresult = false; } } catch (Exception ex) { blresult = false; } return blresult; } /** * 執行命令 */ public bool exec_command(string strcmd) { bool blresult; string strprompt; blresult = false; strprompt = ""; if (telnet_tcp_client.Connected) { blresult = send_data_to_host(strcmd + "/n/r"); if (blresult == false) { return false; } strprompt = ""; strprompt = recv_data_from_host(); if ((strprompt.Trim().LastIndexOf("#") > -1) || (strprompt.Trim().LastIndexOf("$") > -1) || (strprompt.Trim().LastIndexOf(">") > -1)) { blresult = true; return blresult; } } return blresult; } /** * telnet向主機發送數據 */ public bool send_data_to_host(string strcmd) { try { // socket error時、return if (!telnet_tcp_client.Connected) { return false; } byte[] bbuf = System.Text.ASCIIEncoding.ASCII.GetBytes(strcmd.Replace("/0xFF", "/0xFF/0xFF")); telnet_tcp_client.GetStream().Write(bbuf, 0, bbuf.Length); } catch (Exception ex) { return false; } return true; } /** * Telnet從主機接受數據 */ public string recv_data_from_host() { int iinput_data; //data int inputverb; int inputoption; StringBuilder sbtemp; NetworkStream ns_temp; byte[] bread_buffer; StringBuilder sbcomplete_message; int iread_bytes_num; sbtemp = new StringBuilder(); // socket沒有鏈接的時候,返回空 if (!telnet_tcp_client.Connected) { return null; } do { // read 1 byte iinput_data = telnet_tcp_client.GetStream().ReadByte(); switch (iinput_data) { case -1: break; case (int)Verbs.IAC: // 接受的數據有keyword // read 1 byte inputverb = telnet_tcp_client.GetStream().ReadByte(); if (inputverb == -1) break; switch (inputverb) { case (int)Verbs.IAC: sbtemp.Append(inputverb); break; case (int)Verbs.DO: case (int)Verbs.DONT: case (int)Verbs.WILL: case (int)Verbs.WONT: inputoption = telnet_tcp_client.GetStream().ReadByte(); if (inputoption == -1) break; telnet_tcp_client.GetStream().WriteByte((byte)Verbs.IAC); telnet_tcp_client.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT); telnet_tcp_client.GetStream().WriteByte((byte)inputoption); break; default: break; } break; default: sbtemp.Append((char)iinput_data); bread_buffer = new byte[8192]; sbcomplete_message = new StringBuilder(); iread_bytes_num = 0; ns_temp = telnet_tcp_client.GetStream(); if (ns_temp.CanRead) { System.Threading.Thread.Sleep(ilogin_wait_time); iread_bytes_num = ns_temp.Read(bread_buffer, 0, bread_buffer.Length); sbtemp.AppendFormat("{0}", Encoding.ASCII.GetString(bread_buffer, 0, iread_bytes_num)); } break; } // timeout System.Threading.Thread.Sleep(irecv_wait_time); } while (telnet_tcp_client.Available > 0); // 返回接受的數據 return sbtemp.ToString(); } /** * 例子程序 */ public bool exec_sample() { bool blsts; blsts = true; // 關閉Telnet鏈接 this.close_telnet(); // telnet鏈接到遠程主機 blsts = this.open_connect(); if (blsts == false) { return blsts; } // 在遠程主機上執行命令 blsts = this.exec_command("echo ABC"); // 執行失敗 if (blsts == false) { Alert.Show("命令執行失敗"); } return blsts; } //====================================================================================== /// <summary> /// 字符串編碼轉換,解決漢字顯示亂碼問題。 /// 原始字符串中的漢字存儲的是漢字內碼,此代碼實質是將漢字內碼轉換爲GB2312編碼。(夏春濤20110531) /// </summary> /// <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; } //====================================================================================== }