一个不错的C# Telnet类库(改进了汉字显示乱码的问题)

园友e-shizhan给出了一个不错的C# Telnet类库,但存在汉字显示乱码的问题,本人做了改进,希望对大家有用。

原文:http://www.cnblogs.com/eshizhan/archive/2010/06/03/1750661.html?login=1

原文代码:http://files.cnblogs.com/eshizhan/telnet.zip

//调用方法如下:
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);
        /// <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);

            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;
        }
        //======================================================================================
    }
}

 

时间: 2024-09-13 15:45:35

一个不错的C# Telnet类库(改进了汉字显示乱码的问题)的相关文章

【转】一个不错的eclipse反编译插件

在CSDN论坛上看到的一个不错的eclipse反编译插件,感觉看起来不错的样子,因而记下,原网址是:http://topic.csdn.net/u/20121030/14/CDE52930-BAF2-4F88-B751-3797A7EB3C44.html 闲暇之余,写了一个Eclipse下的Java反编译插件:Eclipse Class Decompiler,整合了目前最好的2个Java反编译工具Jad和JD-Core,并且和Eclipse Class Viewer无缝集成,能够很方便的使用本插

一个不错的日期输入控件

控件|日期 一个不错的日期输入控件,希望对你有用 支持日期段,自带多种介面.皮肤样式在themes目录下 下载: 点击这里下载此控件

以前收集的一些资料---(一种新思路)使用一个“静态”的ASP页面来改进你的服务器的性能

服务器|静态|性能|页面 使用一个"静态"的ASP页面来改进你的服务器的性能通常大家显示一个数据库中的信息时都是使用动态页面来生成的,这对于一个小网站或者当数据库内的容量不大时,系统的性能并没有什么影响.但是当用户要频繁地访问一个数据量很大的库时,系统是不是还能够承受得了了.下面介绍一种"静态"ASP技术来解决这个问题.例如现在这个有一个人员资料库,结构如下:ID    First     Last         Company      Email       

最近发现一个不错的网站http://www.ossearch.cn

问题描述 最近发现一个不错的网站http://www.ossearch.cn自己进去看看就知道了.我感觉挺好

co域名怎么样?co是一个不错的选择!

co域名是http://www.aliyun.com/zixun/aggregation/34623.html">哥伦比亚的顶级域名,从开放至今,已经有3年多,用年龄算的话也有3岁半了吧. co域名目前由哥伦比亚一所名为La Universidad de Los Andes的大学管理. 当我们第一眼看到"co"可能会觉得他很特别,其实他确实是一个很很特别的域名. 为什么这么说呢? 1.co域名非常简短,只有两位,容易记忆: 2.co域名和cm域名一样是哥伦比亚国家代码顶

从一个不错的留言本弄的mysql数据库操作类_php技巧

从一个不错的留言本弄的mysql数据库操作类,初学php的朋友可以参考下 复制代码 代码如下: <?php class mysql{     var $querynum = 0;     function connect($dbhost, $dbuser, $dbpw, $dbname = '',$dbcharset='') {         if(!@mysql_connect($dbhost, $dbuser, $dbpw)) {             $this->show('Can

一个不错的OA圈子大量的源码

问题描述 一个不错的OA圈子大量的源码http://q.hexun.com/lotus/default.html 解决方案 解决方案二:该回复于2010-12-06 11:42:44被版主删除解决方案三:DDDDDDDDDDDDD解决方案四:已经收藏了..解决方案五:lotus的B/S成熟办公自动化系统有源码用于学习项目开发的好东东试用下载地址:http://www.brsbox.com/filebox/down/fc/a5d8f8d4a355fe830c9929c37005ffe11.把解压后

silverlight:分享一个不错的自定义布局CollectionFlow(可用于制作相册的哦!)

2009年最后一天,分享一个不错的Silverlight开源自定义布局:CollectionFlow,原作者博客: http://mentas.spaces.live.com/Blog/cns!A8D899E9B03A6E15!784.entry 上面还有源代码下载 效果图:   简单分析下原理: 先将所有元素堆叠在布局横向中心点,然后找到当前显示的元素,将其作为分界点,将其它元素分成左右二堆,依次移动变化 为了方便大家测试各参数的效果,我还做了一个小demo: http://images.24

比特币仍然是一个不错的投资标

11月初,一枚比特币的价格是200多美元,但到了月末,价格已经暴涨4倍超过1000多美元.不过,在扎身比特币领域创业的校内网(现改名人人网)联合创始人杨曜睿看来,如果能够长期持有,比特币仍然是一个不错的投资标的. 2013年5月,杨曜睿发起创立了ASICME公司,主营可以挖到比特币的"矿机"生意,公司成立不到一个月,销售额就达到500万元. 6月份时,杨曜睿对出售"矿机"生意还信心满满,但等到11月28日,再次见到他时,他已经不愿继续出售"矿机"