您好,登錄后才能下訂單哦!
C# Telnet 類庫代碼
本文鏈接:
https://blog.51cto.com/2193967/2440126
今天給筆記本裝了固態,速度提升很明顯,很高興,發個微博留作紀念。
自學的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;
}
}
}
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。