Socket实现telnet交互通信

来源:互联网 发布:淘宝巫师鞋柜是正品吗 编辑:程序博客网 时间:2024/06/18 11:20

using System;
usingSystem.Collections.Generic;
using System.Text;
using System.Net;
usingSystem.Net.Sockets;
usingSystem.Collections;
using System.Threading;

namespace TelnetDemo
{
   public class Telnet
   {
       #region telnet的数据定义  
       // 标志符,代表是一个TELNET 指令
       readonly Char IAC = Convert.ToChar(255);
       // 表示一方要求另一方使用,或者确认你希望另一方使用指定的选项。
       readonly Char DO = Convert.ToChar(253);
       // 表示一方要求另一方停止使用,或者确认你不再希望另一方使用指定的选项。   
       readonly Char DONT = Convert.ToChar(254);
       // 表示希望开始使用或者确认所使用的是指定的选项。
       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';
       // 流
       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, intCommandTimeout)
       {
           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 = newAsyncCallback(OnRecievedData);
               s.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None,recieveData, s);

               return true;
           }
           catch (Exception)
           {
               return false;
           }
       }

       ///        
       ///当接收完成后,执行的方法(供委托使用)       
       ///      
       ///       
       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接收到的字符                 
                   
                   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 { }
       }
       ///        
       /// 发送数据的函数       
       ///        
       private void RespondToOptions()
       {
           try
           {
               //声明一个字符串,来存储接收到的参数            
               string strOption;
               
               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);

           }
       }
       ///        
       ///解析接收的数据,生成最终用户看到的有效文字,同时将附带的参数存储起来       
       ///       
       ///收到的处理后的数据     
       ///     
       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;
       }
       ///      
       ///获得IP地址      
       ///       
       ///    
       ///   
       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)
                   {
                       
                       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);
           }
       }
       ///     
       /// 将信息转化成charp[] 流的形式,使用socket进行发出   
       ///发出结束之后,使用一个匿名委托,进行接收,  
       /// 之后这个委托里,又有个委托,意思是接受完了之后执行OnRecieveData方法 
       ///       
       ///       
       ///   
       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 = newAsyncCallback(OnRecievedData);

                       sock1.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None,recieveData, sock1);
                      
                   }
               }, s);

               s.EndSend(ar2);
           }
           catch (Exception ers)
           {
               Console.WriteLine("出错了,在回发数据的时候:" + ers.Message);
           }
       }

       ///
       /// 等待指定的字符串返回
       ///
       /// 等待的字符串
       /// 返回0
       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

       ///
       /// 取完整日志
       ///
       public string SessionLog
       {
           get
           {
               return strFullLog;
           }
       }

      

       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 p = new Telnet("192.168.1.100"2350);
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);

0 0