C# Telnet 类库代码 今天给笔记本装了固态,速度提升很明显,很高兴,发个微博留作纪念。 自学的C#,干网络的,想弄个工具方便日常工作,想自己实现批量操作的工具。 想起来很简单
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; } }
}