C# Telnet 类库代码,谨献给还在寻找C# Telnet类库的兄弟

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# Telnet 类库代码,谨献给还在寻找C# Telnet类库的兄弟相关的知识,希望对你有一定的参考价值。

C# Telnet 类库代码 今天给笔记本装了固态,速度提升很明显,很高兴,发个微博留作纪念。 自学的C#,干网络的,想弄个工具方便日常工作,想自己实现批量操作的工具。 想起来很简单做起来很费劲。网上一顿乱查。 这里感谢“Telnet 非常实用的类库 - 王小壮的博客 - CSDN博客” https://blog.csdn.net/weixin_42183571/article/details/80783268

这个文章给了我方向,代码看了N遍,有了很多启发。
那里面的异步没看懂,当时不懂异步,看着和天数一样。。。。
什么协议都是有协商的过程:
telnet协商代码 取自 王小壮 ,有一小点的修改。

多了不BB,上代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace  项目名字

        public  class Telnet
        
    #region // 协商的命令码直接复制

    // private byte[] receivebytes = new byte[1024]; //接受
    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‘;

    private ArrayList m_ListOptions = new ArrayList();
    enum Verbs  WILL = 251, WONT = 252, DO = 253, DONT = 254, IAC = 255 
    enum Options  RD = 1, SGA = 3 
    private Socket sock = null;  // 定义全局变量,方便调用

            //定义事件
    private static ManualResetEvent connectDone = new ManualResetEvent(false);
    private static ManualResetEvent receiveDone = new ManualResetEvent(false);

    private static ManualResetEvent sendDone = new ManualResetEvent(false);
    // private static ManualResetEvent waitDone = new ManualResetEvent(false);
    // private string str_data = "";// 接受到的数据
    private StringBuilder str_data = new StringBuilder();// 所有收到的数据
    private StringBuilder Str_Temp = new StringBuilder();//单个命令的执行结果,不完整实际没有用
    public bool Connected //连接状态
    
        get
        

            if (sock != null)
            
                return sock.Connected;
            
            else
            
                return false;
            

        
        // get return = sock.Connected;
        // set; 
    
    public string RecData //取得执行的log
    
        //get
        //    return str_data;
        //
        get
        
            return str_data.ToString();
        

    
    public string TempData //取得每个命令执行的log,有问题,没有用
    
        //get
        //    return str_data;
        //
        get
        
            return Str_Temp.ToString();
        

    

    #endregion

    public Telnet(string IP)
        : this(IP, 23)   //构造函数链
    public Telnet(string IP, int port)// 工作主函数
    
        //IPAddress ipAddress = IPAddress.Parse("192.168.56.2");
        IPAddress ipAddress = IPAddress.Parse(IP);
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
        sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        Console.WriteLine("connectDone.Rese : 设置为无信号开始连接");
        connectDone.Reset();
        sock.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), sock);// 异步连接
        connectDone.WaitOne(10000, false);//异步阻断,连接成功就进入下异步,加入连接超时间10S,

        if (!connectDone.WaitOne(10000, false))
        
            sock.Close();//连接失败,超时退出;   
            Console.WriteLine("连接失败,超时退出");
        
        else   //  如果连接成功,开始接受数据。
        
            //开始异步接受的线程
            Thread threadread = new Thread(new ThreadStart(Receive));
            threadread.Start();
        

    
    /// <summary>
    /// 用户名密码默认回显为“:”
    /// </summary>
    /// <param name="IP"></param>
    /// <param name="port"></param>
    /// <param name="username"></param>
    /// <param name="passwd"></param>
    public Telnet(string IP, int port,string username,string passwd)// 工作主函数  
     :this(IP, port, username, passwd, ":", ":") //这里的两个冒号是登录连接设备的回显,
    

    
    public Telnet(string IP, int port, string username, string passwd,string usernamewait,string passwdwait)// 工作主函数
    
        //IPAddress ipAddress = IPAddress.Parse("192.168.56.2");
        IPAddress ipAddress = IPAddress.Parse(IP);
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
        sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        Console.WriteLine("connectDone.Rese : 设置为无信号开始连接");
        connectDone.Reset();
        sock.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), sock);// 异步连接
        connectDone.WaitOne(10000, false);

        if (!connectDone.WaitOne(10000, false))
        
            sock.Close();//连接失败,超时退出;   
            Console.WriteLine("连接失败,超时退出");
        
        else   //  如果连接成功,开始接受数据。
        
            //开始异步接受的线程
            Thread threadread = new Thread(new ThreadStart(Receive));
            threadread.Start();
            Send(usernamewait, username + "\r"); //自动连接配置,可以手动
            Send(passwdwait, passwd + "\r");
        

    
    public Telnet(string IP, int port,int timeout)// 工作主函数
    
        //IPAddress ipAddress = IPAddress.Parse("192.168.56.2");
        IPAddress ipAddress = IPAddress.Parse(IP);
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
        sock = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        Console.WriteLine("connectDone.Rese : 设置为无信号开始连接");
        connectDone.Reset();
        sock.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), sock);// 异步连接
        connectDone.WaitOne(timeout*1000, false);

        if (!connectDone.WaitOne(timeout * 1000, false))
        
            sock.Close();//连接失败,超时退出;   
            Console.WriteLine("连接失败,超时退出");
        
        else   //  如果连接成功,开始接受数据。
        
            //开始异步接受的线程
            Thread threadread = new Thread(new ThreadStart(Receive));
            threadread.Start();
        

    
    /// <summary>
    /// 异步连接,异步回调
    /// </summary>
    /// <param name="ar"></param>
    private void ConnectCallback(IAsyncResult ar)
    
        try
        
            // Retrieve the socket from the state object.  
            Socket client = (Socket)ar.AsyncState;

            // Complete the connection.  
            client.EndConnect(ar);
            //Console.WriteLine("Socket connected to 0",
            //    client.RemoteEndPoint.ToString());

            //// Signal that the connection has been made.  
            Console.WriteLine(" connectDone.Set:设置为有信号"); 
            connectDone.Set();  // 这里是把连接的事件作为 有信号,主函数中的阻断会继续执行,否则10s超时了
        
        catch (Exception e)
        
            Console.WriteLine("异步连接:" + e.ToString());
        
    
            //这里是接受数据的方法
    private void Receive()
    
        try
        
          //  receiveDone.Reset();
                        //接受没有用阻断,这里用的是循环接受,是在回调中实现
            // Create the state object.  
                            // 这里是查了msdn文档,有这个,主要进是传递 这个辅助类中主要是传递 socke 与 state.buffer
            StateObject state = new StateObject(); 
            state.workSocket = sock;

            // Begin receiving the data from the remote device.  
            sock.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);
          //  receiveDone.WaitOne(10000,false);
            //string ddd = string.Empty;
        
        catch (Exception e)
        
            Console.WriteLine(e.ToString());
        
    
            //接受数据异步回调
    private void ReceiveCallback(IAsyncResult ar)
    
        try
        
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;
            //取得 sock 其实没必要,因为socket 是全局的变量
            int bytesRead = client.EndReceive(ar);

            if (bytesRead > 0)
            
                            ////创建一个与接受 数据大小一致的 byte[] 
                                        Byte[] mToProcess = new Byte[bytesRead];  
                Array.Copy(state.buffer, mToProcess, bytesRead);//数组复制

                Str_Temp = ProcessOptions(mToProcess);//清洗掉命令码,取得回显的实际数据
                                                      //  Console.WriteLine("*********************************************");
                Console.Write(Str_Temp);//回显本次的数据
                                        // Console.WriteLine("*********************************************");
                                        // response = mOutText;
                str_data.Append(Str_Temp);  //所有的数据,把每次的接受的数据添加到 str_data.用于后期保存log
                string temps = str_data.ToString();//这里是测试每次取得数据的断点,懒得删

                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReceiveCallback), state);

                //下面进是循环取得数据的关键,每次接受完数据,又开始新的接受数据。
            
            else
            

                //这里是接受数据为0,正常异步接受到的数据为0时应该没有数据阻断状态,不会到这个地方。 
                Console.WriteLine("接受数据小于0:" + "发生异常");
                client.Close();//关闭连接
            
        
        catch (Exception e)
        
            Console.WriteLine("接受数据异常" + e.ToString());//这里是抓取异常的代码。一般telnet结束时,异步还处理阻断状态,有时会发送,没啥用。
        
    

            // 发送命令的方法,这里的data 是没有 \r 回车的,
    public void Send(String data)
    
        try
        

            byte[] byteData = Encoding.ASCII.GetBytes(data);
            //异步发送
                                            sock.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), sock);
                                    //异步阻断,发送完成后继续执行,未完成进一只阻断,可以加超时时间   sendDone.WaitOne(10000,false);
                                    sendDone.WaitOne();

        
        catch
        
            Console.WriteLine("出现异常:0", data + "\r\n");
        
        finally
        
            //client.Shutdown(SocketShutdown);
        
    
    public void Send(char data)
    
        char[] char_data =  data ;
        try
        
            byte[] byteData = Encoding.ASCII.GetBytes(char_data);
            sock.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), sock);
            sendDone.WaitOne();
        
        catch
        
            Console.WriteLine("出现异常:0", data + "\r\n");
        
        finally
        
            //client.Shutdown(SocketShutdown);
        
    
    public void Send(char[] data)
    

        try
        
            byte[] byteData = Encoding.ASCII.GetBytes(data);
            sock.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), sock);
            sendDone.WaitOne();
        
        catch
        
            Console.WriteLine("出现异常:0", data + "\r\n");
        
        finally
        
            //client.Shutdown(SocketShutdown);
        
    
    public void Send(byte[] byteData)
    

        try
        
            //byte[] byteData = Encoding.ASCII.GetBytes(data);
            sock.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), sock);
            sendDone.WaitOne();
        
        catch
        
            Console.WriteLine("发送出现异常bytedata:0", byteData.ToString() + "\r\n");
        
        finally
        
            //client.Shutdown(SocketShutdown);
        
    
    /// <summary>
    /// 执行命令的方法,判断回显字符,执行命令
    /// </summary>
    /// <param name="expect">回显字符串判断</param>
    /// <param name="command">判定成功执行的命令</param>
    /// <param name="delay">执行命令的延迟</param>
    public void Send(string expect, string command, int delay_time)
    
        // string local_data = string.Empty ;
        int i = 0;

        while (true)
        
        //这里主要是判断 str_data 中的数据,接受的数据会持续写入到 这个中,接受可能有延迟,加循环判断,
            if (str_data.ToString().TrimEnd().EndsWith(expect))
            
                Thread.Sleep(delay_time);//执行命令的延迟
                Send(command);
                break;

            
            else
            // 增加命令执行代码判断失败的惩罚值,8次后,当前接受的数据中还没有出现判断的字符串,socket关闭
                i++;
            
            Thread.Sleep(1000);//  如果当前的接受数据中没有判断的字符出现,等待1s后再次判断
            if(i==8)
            
                //Console.WriteLine("命令执行错误,错误命令" + command + "命令执行结果:");// + str_data.ToString());
                //Console.WriteLine();
                // Console.WriteLine("命令执行错误,错误命令"+ command+"命令执行结果:" +str_data.ToString());
               // str_data.Append("异常终结");
                sock.Close ();
                break;

            
            //    

        
    
    /// <summary>
    /// 执行命令的方法,默认延迟500ms。
    /// </summary>
    /// <param name="expect">回显字符串判断</param>
    /// <param name="command">判定成功执行的命令</param>
    public void Send(string expect, string command)
    
        Send(expect, command, 100);

    

    private static void SendCallback(IAsyncResult ar)
    //异步发送数据
        try
        
            Socket client = (Socket)ar.AsyncState;
            int bytesSent = client.EndSend(ar);
            sendDone.Set();

        
        catch (Exception e)
        
            Console.WriteLine(e.ToString());
        
    

    /// <summary>
    /// 清洗命令码。telnet的核心代码
    /// </summary>
    /// <param name="resbyte"></param>
    /// <returns></returns>
    private StringBuilder ProcessOptions(byte[] resbyte)
    //网络所得

        string m_DISPLAYTEXT = "";
        string m_strTemp = "";
        //string m_strOption = "";
        //string m_strNormalText = "";
        StringBuilder m_strNormalText = new StringBuilder();
        bool bScanDone = false;
        int ndx = 0;
        int ldx = 0;
        char ch, canshu;
        try
        
            //把数据从byte[] 转化成string
            for (int i = 0; i < resbyte.Length; i++)
            
                Char ss = Convert.ToChar(resbyte[i]);
                m_strTemp = m_strTemp + Convert.ToString(ss);
            

            //此处意义为,当没描完数据前,执行扫描
            while (bScanDone != true)
            
                //获得长度
                int lensmk = m_strTemp.Length;
                //之后开始分析指令,因为每条指令为255 开头,故可以用此来区分出每条指令
                ndx = m_strTemp.IndexOf(Convert.ToString(IAC));//首次出现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];//获取命令码
                    canshu = m_strTemp[ndx + 2];//获取命令码

                    //如果命令码是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;
                        telnetproceess(ch, canshu);
                    
                    //如果IAC后面又跟了个IAC (255)
                    else if (ch == IAC)
                    
                        //则显示从输入的字符串头开始,到之前的IAC 结束
                        //m_DISPLAYTEXT += m_strTemp.Substring(0, ndx);
                        //之后将处理过的字符串排除出去
                        m_strTemp = m_strTemp.Substring(ndx + 1);
                        string xxc = m_strTemp;
                    
                    //如果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 + 1);
                        telnetproceess(ch, canshu);
                    

                    #endregion
                
                //若字符串里已经没有IAC标志位了
                else
                
                    //显示信息累加上m_strTemp存储的字段
                    m_DISPLAYTEXT = m_DISPLAYTEXT + m_strTemp;
                    bScanDone = true;
                
            
            //输出人看到的信息
            m_strNormalText.Append(m_DISPLAYTEXT);
        
        catch (Exception eP)
        
            throw new Exception("解析传入的字符串错误:" + eP.Message);
        
        return m_strNormalText;

    
            //异步委托调用,这里是协商代码,测试很多设备 占时没发现什么问题
    private void telnetproceess(char ch, char canshu)
    
        //如果命令码是253(DO) 254(DONT)  251(WILL) 252(WONT) 的情况下
        if (ch == DO)//253 对端设备对本端设备的发出参数请求(如果支持对方的,则DO确认,不支持则DONT)
        
            if (canshu == 32 || canshu == 35 || canshu == 39 || canshu == 36)
            
                //发送命令码
                byte[] sendcom = new byte[3];
                sendcom[0] = 255;
                sendcom[1] = 252;
                sendcom[2] = Convert.ToByte(canshu);
                //tcpSocket.Send(sendcom);
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);

            
            else if (canshu == 24)
            
                //byte[] sendcom =  255, 240, 78, 65, 87, 83, 32, 8, 0, 32, 2, 5, 255, 240 ;
                byte[] sendcom =  255, 251, 24 ;
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);
            
            else if (canshu == 31)
            /*
                255 250 31 window size
                255 240 开始
                recv SB N78A65W87S83 8(56)0(48) 2(50)5(53)
                */
                byte[] sendcom1 =  255, 251, 31 ;//同意窗口大小请求
                Send(sendcom1);
                //steam.Write(sendcom1, 0, sendcom1.Length);
                byte[] sendcom =  255, 250, 31, 0, 80, 0, 25, 255, 240 ;//发送窗口大小
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);
                //byte[] sendcoms =  255, 250, 31,78, 65,87,83, 32,56,48,32,50,53,255,240;
                //byte[] sendcoms =  255, 250, 31, 78, 65, 87, 83, 255, 240 ;
                //sock.Send(sendcoms);
            
            else if (canshu == 33)
            

            
            else if (canshu == 1 || canshu == 34)
            
                // 255 252 1        echo
                byte[] sendcom =  255, 252, Convert.ToByte(canshu) ;
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);
            

            else
            
                string cuowu = canshu.ToString();//
                string xx;//拍错用
            

        
        else if (ch == DONT)//254 协商对端设备的参数(对端发出对本端参数的请求)
        

        
        else if (ch == WILL)//251 本端设备的参数(发送给对方)
        
            if (canshu == 3)
            
            
            else if (canshu == 1 || canshu == 3)
            
                //255 253 echo;
                byte[] sendcom =  255, 253, Convert.ToByte(canshu) ;
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);
            
            else
            
                //255 254 status 
                byte[] sendcom =  255, 254, Convert.ToByte(canshu) ;
                //steam.Write(sendcom, 0, sendcom.Length);
                Send(sendcom);
            

        
        else if (ch == WONT)//252 本端设备的参数(发送给对方)
        

        
        else if (ch == 250)
        
            if (canshu == 24)
            
                //byte[] sendcom =  255, 254, Convert.ToByte(canshu) ;
                // recv SB 24 0 ANSI
                byte[] sendcom =  255, 250, 24, 0, 65, 78, 83, 73, 255, 240 ;//发送终端编码send SB 24 0  ANSI
                Send(sendcom);
                //steam.Write(sendcom, 0, sendcom.Length);
                // recv SB 65 87 S[Unrecognized]
                //recv SB NAWS 80 25
                // IAC,SB,24,0,‘I‘,‘B‘,‘M‘,‘P‘,‘C‘, IAC,SE

            
        

    

    /// <summary>
    /// 保存log文件.这里个人需要,操作的就是本次的 str_data  
    /// </summary>
    /// <param name="FilePathName">绝对文件路径</param>
    public void Save_File(string FilePathName)
    
        string[] Str_dir = FilePathName.Split(‘\\‘);
        string FilePath_Dir = string.Empty;
        for (int i = 0; i < Str_dir.Length - 1; i++)
        
            FilePath_Dir += Str_dir[i] + "\\";
        
        string FileName = Str_dir[Str_dir.Length - 1];
        Save_File(FilePath_Dir, FileName);
    

    /// <summary>
    /// 判断文件或目录是否存在
    /// </summary>这是保存到当前程序运行到目录以今天日期生成目录
    /// <param name="FilePath_Dir">目录</param>
    /// <param name="FileName">文件名称</param>
    private void File_Exists(string FilePath_Dir, string FileName)
    

        string FilePathName = FilePath_Dir + "\\" + FileName;
        // string path = FilePath_Dir + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\" + FilePathName + ".txt";
        if (!Directory.Exists(FilePath_Dir))
        
            Directory.CreateDirectory(FilePath_Dir);
        

        if (!File.Exists(FilePathName))
        
            FileStream fs = File.Create(FilePathName);
            fs.Close();
        
    
    /// <summary>
    /// 保存文件
    /// </summary>
    /// <param name="FilePath_Dir">文件的目录</param>
    /// <param name="FileName">文件的名称</param>
    public void Save_File(string FilePath_Dir, string FileName)//保存文件函数
    
        string FilePathName = FilePath_Dir + "\\" + FileName;
        File_Exists(FilePath_Dir, FileName);
        StreamWriter steam = new StreamWriter(FilePathName, true, Encoding.UTF8);
        steam.Write(str_data);
        steam.Close();
    
    public void close()
    
        try
        
            sock.Shutdown(SocketShutdown.Both);
            sock.Close();
        
        catch  
    
    //辅助类传递 接收数据,与socket
    public class StateObject
    
        // Client socket.  
        public Socket workSocket = null;
        // Size of receive buffer.  
        public const int BufferSize = 1024;
        // Receive buffer.  
        public byte[] buffer = new byte[BufferSize];
        // Received data string.  
        public StringBuilder sb = new StringBuilder();
        public string waitfor = string.Empty;
    

以上是关于C# Telnet 类库代码,谨献给还在寻找C# Telnet类库的兄弟的主要内容,如果未能解决你的问题,请参考以下文章

PAT1028

C#/.NET第六期01C#基础_01:运行环境新建C#程序的流程严格区分大小写类库的含义

c#怎样创建类库

C# Telnet 库

C#通用节气类库农历类(很全面)

C#/.NET第六期01C#基础_01:运行环境新建C#程序的流程严格区分大小写类库的含义