C#实现TFTP客户端的项目实践
作者:大浪淘沙胡
TFTP不仅有断点续传,多用户级别限制等功能,本文主要介绍了C#实现TFTP客户端的项目实践,具有一定的参考价值,感兴趣的可以了解一下
1、文件结构
2、TftpConfig.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TftpTest { public class TftpConfig { } /// <summary> /// 模式 /// </summary> public enum Modes { NETASCII = 0, //ASCII OCTET = 1 //二进制 } /// <summary> /// 操作码 /// </summary> public enum OpCodes { RRQ = 1, // 读请求 WRQ = 2, // 写请求 DATA = 3, // 数据 ACK = 4, // Acknowledge ERROR = 5, // 错误 OACK = 6 // Option Acknowledge } public enum TransferType { Get, Put } public struct TransferOptions { private TransferType _action; public TransferType Action { get { return _action; } set { _action = value; } } private string _localFilename; public string LocalFilename { get { return _localFilename; } set { _localFilename = value; } } private string _remoteFilename; public string RemoteFilename { get { return _remoteFilename; } set { _remoteFilename = value; } } private string _host; public string Host { get { return _host; } set { _host = value; } } } }
3、ErrorPacket.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TftpTest { public class ErrorPacket { short _code; public short Code { get { return _code; } set { _code = value; } } string _message; public string Message { get { return _message; } set { _message = value; } } public ErrorPacket(short Code, string Message) { Code = _code; Message = _message; } } }
4、PacketBuilder.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TftpTest { public class PacketBuilder { public byte[] Request(OpCodes OpCode, string RemoteFileName, Modes Mode, int BlockSize, long TransferSize, int Timeout) { // Request packet structure // ----------------------------------------------------------------------------- // |OpCode|FileName|0|Mode|0|BlkSize|0|BSVal|0|TSize|0|TSVal|0|Timeout|0|TVal|0| // ----------------------------------------------------------------------------- int len; string packetStr = ""; string mode = Mode.ToString().ToLower(); string blockSize = BlockSize.ToString(); string nullChar = "\0"; byte[] packet; // Create packet as a string switch (OpCode) { case OpCodes.RRQ: packetStr = nullChar + (char)1; break; case OpCodes.WRQ: packetStr = nullChar + (char)2; break; } packetStr += RemoteFileName + nullChar + mode + nullChar + "blksize" + nullChar + BlockSize.ToString() + nullChar + "tsize" + nullChar + TransferSize.ToString() + nullChar + "timeout" + nullChar + Timeout.ToString() + nullChar; len = packetStr.Length; packet = new byte[len]; // Encode packet as ASCII bytes packet = System.Text.Encoding.ASCII.GetBytes(packetStr); return packet; } public byte[] Ack(int Block1, int Block2) { // ACK packet structure // ---------- // |04|Block| // ---------- byte[] packet = new byte[4]; packet[0] = 0; packet[1] = (byte)OpCodes.ACK; packet[2] = (byte)Block1; packet[3] = (byte)Block2; return packet; } public byte[] Data(byte[] SendData, int Block1, int Block2) { // DATA packet structure // ---------- // |03|Block| // ---------- byte[] packet = new byte[SendData.Length + 4]; //packet[0] = 0; packet[1] = (byte)OpCodes.DATA; packet[2] = (byte)Block1; packet[3] = (byte)Block2; for (int h = 4; h < SendData.Length + 4; h++) { packet[h] = SendData[h - 4]; } return packet; } public int[] IncrementBock(byte[] ReceivedData, int[] Block) { if (ReceivedData[3] == 255) { if (ReceivedData[2] < 255) { Block[0] = (int)ReceivedData[2] + 1; Block[1] = 0; } else { Block[0] = 0; Block[1] = 0; } } else { Block[1] = (int)ReceivedData[3] + 1; } return Block; } } }
5、PacketReader.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TftpTest { public class PacketReader { public OpCodes ReadOpCode(byte[] ReceivedData) { return (OpCodes)ReceivedData[1]; } public int ReadTransferSize(byte[] ReceivedData) { int h, tSize = 0; string searchStr, decPacket = Encoding.ASCII.GetString(ReceivedData); char[] splitChar = { '\0' }; string[] splitPacket = decPacket.Split(splitChar); for (h = 0; h < splitPacket.Length - 1; h++) { searchStr = splitPacket[h].ToLower(); if (searchStr == "tsize") { tSize = int.Parse(splitPacket[h + 1]); } } return tSize; } public ErrorPacket ReadError(byte[] ReceivedData) { string codeStr = ReceivedData[2].ToString() + ReceivedData[3].ToString(); short code = short.Parse(codeStr); string message = ""; for (int h = 4; h < ReceivedData.Length; h++) { if (ReceivedData[h] == 0) break; message += (char)ReceivedData[h]; } return new ErrorPacket(code, message); } public bool CompareBlocks(byte[] SentData, byte[] ReceivedData) { if (ReceivedData[2] == SentData[2] && ReceivedData[3] == SentData[3]) return true; return false; } } }
6、TftpClient.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading.Tasks; namespace TftpTest { public class TftpClient { // 委托 public delegate void ConnectedHandler(); public delegate void TransferringHandler(long BytesTransferred, long BytesTotal); public delegate void TransferFailedHandler(short ErrorCode, string ErrorMessage); public delegate void TransferFinishedHandler(); public delegate void DisconnectedHandler(); // 事件 public event ConnectedHandler Connected; public event TransferringHandler Transferring; public event TransferFailedHandler TransferFailed; public event TransferFinishedHandler TransferFinished; public event DisconnectedHandler Disconnected; private string _host; [Description("主机")] public string Host { get { return _host; } set { _host = value; } } private Modes _mode = Modes.OCTET; [Description("模式")] public Modes Mode { get { return _mode; } set { _mode = value; } } private int _blockSIze = 512; [Description("块大小")] public int BlockSize { get { return _blockSIze; } set { _blockSIze = value; } } private int _timeout = 10; [Description("溢出时间")] public int Timeout { get { return _timeout; } set { _timeout = value; } } PacketReader _packetReader = new PacketReader(); PacketBuilder _packetBuilder = new PacketBuilder(); public bool Get(string localFile, string RemoteFile, string Host) { return Get(localFile, RemoteFile, Host, Mode, BlockSize, Timeout); } public bool Put(string localFile, string RemoteFile, string Host) { return Put(localFile, RemoteFile, Host, Mode, BlockSize, Timeout); } public bool Get(string LocalFile, string RemoteFile, string Host, Modes Mode, int BlockSize, int Timeout) { int recvLen, remoteFileSize = 0, buffer = BlockSize + 4; long bytesReceived = 0; BinaryWriter BWriter = new BinaryWriter(File.Open(LocalFile, FileMode.Create)); OpCodes opCode = new OpCodes(); IPHostEntry hInfo = Dns.GetHostEntry(Host); IPAddress address = hInfo.AddressList[0]; IPEndPoint remoteEP = new IPEndPoint(address, 69); EndPoint localEP = (remoteEP); Socket UDPSock = new Socket (remoteEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); // Create initial request and buffer for response byte[] sendData = _packetBuilder.Request(OpCodes.RRQ, RemoteFile, Mode, BlockSize, 0, Timeout); byte[] recvData = new byte[BlockSize + 4]; UDPSock.ReceiveTimeout = Timeout * 1000; // Send request and wait for response UDPSock.SendTo(sendData, remoteEP); recvLen = UDPSock.ReceiveFrom(recvData, ref localEP); // Get TID remoteEP.Port = ((IPEndPoint)localEP).Port; // Invoke connected event Connected.Invoke(); while (true) { // Read opcode opCode = _packetReader.ReadOpCode(recvData); // DATA packet if (opCode == OpCodes.DATA) { bytesReceived += recvLen - 4; // Invoke Transferring Event Transferring.Invoke(bytesReceived, remoteFileSize); for (int h = 4; h < recvLen; h++) { BWriter.Write(recvData[h]); } sendData = _packetBuilder.Ack(recvData[2], recvData[3]); // Check if this packet is the last if (recvLen < buffer) { // Send final ACK UDPSock.SendTo(sendData, remoteEP); // Invoked TransferFinished Event TransferFinished.Invoke(); break; } } // OACK packet else if (opCode == OpCodes.OACK) { remoteFileSize = _packetReader.ReadTransferSize(recvData); sendData = _packetBuilder.Ack(0, 0); } // ERROR packet else if (opCode == OpCodes.ERROR) { ErrorPacket transferError = _packetReader.ReadError(recvData); TransferFailed.Invoke(transferError.Code, transferError.Message); break; } // Send next packet UDPSock.SendTo(sendData, remoteEP); recvLen = UDPSock.ReceiveFrom(recvData, ref localEP); remoteEP.Port = ((IPEndPoint)localEP).Port; } BWriter.Close(); UDPSock.Close(); // Invoke Disconnected Event Disconnected.Invoke(); return true; } public bool Put(string LocalFile, string RemoteFile, string Host, Modes Mode, int BlockSize, int Timeout) { int[] block = new int[2]; int bufferSize = BlockSize; long fileSize, bytesSent = 0; BinaryReader BReader = new BinaryReader(File.Open(LocalFile, FileMode.Open)); FileInfo sendFile = new FileInfo(LocalFile); OpCodes opCode = new OpCodes(); IPHostEntry hostInfo = Dns.GetHostEntry(Host); IPAddress address = hostInfo.AddressList[0]; IPEndPoint remoteEP = new IPEndPoint(address, 69); EndPoint localEP = (remoteEP); Socket UDPSock = new Socket (remoteEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); // Retrieve filesize for tsize option fileSize = sendFile.Length; // Create initial request and buffer for response byte[] sendData = _packetBuilder.Request(OpCodes.WRQ, RemoteFile, Mode, BlockSize, fileSize, Timeout); byte[] recvData = new byte[bufferSize]; UDPSock.ReceiveTimeout = Timeout * 1000; // Send request and wait for response UDPSock.SendTo(sendData, remoteEP); UDPSock.ReceiveFrom(recvData, ref localEP); //Get TID remoteEP.Port = ((IPEndPoint)localEP).Port; // Invoke Connected Event Connected.Invoke(); while (true) { // Read opcode opCode = _packetReader.ReadOpCode(recvData); // ACK packet if (opCode == OpCodes.ACK) { block = _packetBuilder.IncrementBock(recvData, block); sendData = BReader.ReadBytes(bufferSize); bytesSent += sendData.Length; // Invoke Transferring Event Transferring.Invoke(bytesSent, fileSize); sendData = _packetBuilder.Data(sendData, block[0], block[1]); // Check if this packet is the last if (sendData.Length < bufferSize + 4) { // Send final data packet and wait for ack while (true) { UDPSock.SendTo(sendData, remoteEP); UDPSock.ReceiveFrom(recvData, ref localEP); remoteEP.Port = ((IPEndPoint)localEP).Port; // Check the blocks and break free if equal if (_packetReader.CompareBlocks(sendData, recvData)) break; } // Invoke TransferFinished Event TransferFinished.Invoke(); break; } } // OACK packet else if (opCode == OpCodes.OACK) { sendData = BReader.ReadBytes(bufferSize); sendData = _packetBuilder.Data(sendData, 0, 1); bytesSent += sendData.Length - 4; // Invoke Transferring Event Transferring.Invoke(bytesSent, fileSize); if (fileSize == 0) { // Invoke TransferFinished Event TransferFinished.Invoke(); break; } else { // Check if this packet is the last if (sendData.Length < bufferSize + 4) { // Send final data packet and wait for ack while (true) { UDPSock.SendTo(sendData, remoteEP); UDPSock.ReceiveFrom(recvData, ref localEP); remoteEP.Port = ((IPEndPoint)localEP).Port; // Check the blocks and break free if equal if (_packetReader.CompareBlocks(sendData, recvData)) break; } // Invoke TransferFinished Event TransferFinished.Invoke(); break; } } } else if (opCode == OpCodes.ERROR) { ErrorPacket transferError = _packetReader.ReadError(recvData); TransferFailed.Invoke(transferError.Code, transferError.Message); break; } // Send next packet UDPSock.SendTo(sendData, remoteEP); UDPSock.ReceiveFrom(recvData, ref localEP); remoteEP.Port = ((IPEndPoint)localEP).Port; } BReader.Close(); UDPSock.Close(); // Invoke Disconnected Event Disconnected.Invoke(); return true; } } }
7、MainForm.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace TftpTest { public partial class MainForm : Form { private TftpClient tftpClient = new TftpClient(); private delegate void ProgressBarDelegate(int Maximum, int Value); private delegate void TransferButtonDelegate(bool Enabled); public MainForm() { InitializeComponent(); tftpClient.Connected += new TftpClient.ConnectedHandler(TftpClient_Connected); tftpClient.Transferring += new TftpClient.TransferringHandler(TftpClient_Transferring); tftpClient.TransferFailed += new TftpClient.TransferFailedHandler(TftpClient_TransferFailed); tftpClient.TransferFinished += new TftpClient.TransferFinishedHandler(TftpClient_TransferFinished); tftpClient.Disconnected += new TftpClient.DisconnectedHandler(TftpClient_Disconnected); } private void TftpClient_Connected() { TransferButtonDelegate tBtnDel = new TransferButtonDelegate(TransferBtnDelegateFunction); btnTest.Invoke(tBtnDel, false); Console.WriteLine("Connected"); } private void TransferBtnDelegateFunction(bool Enabled) { lock (btnTest) { btnTest.Enabled = Enabled; } } private void TftpClient_Transferring(long BytesTransferred, long BytesTotal) { if (BytesTotal != 0) { ProgressBarDelegate progressBarDel = new ProgressBarDelegate(ProgressBarDelegateFunction); progressBarTftp.Invoke(progressBarDel, new object[2] { (int)(BytesTotal / 10), (int)(BytesTransferred / 10) }); Console.Write("{0}/{1} Bytes Transferred\r", BytesTransferred, BytesTotal); } else Console.Write("."); } private void ProgressBarDelegateFunction(int Maximum, int Value) { lock (progressBarTftp) { try { progressBarTftp.Maximum = Maximum; progressBarTftp.Value = Value; } catch (Exception e) { Console.WriteLine(e.ToString()); } } } private void TftpClient_TransferFailed(short ErrorCode, string ErrorMessage) { Console.WriteLine("Error {0}: {1}", ErrorCode, ErrorMessage); } private void TftpClient_TransferFinished() { ProgressBarDelegate progressBarDel = new ProgressBarDelegate(ProgressBarDelegateFunction); progressBarTftp.Invoke(progressBarDel, new object[2] { 0, 0 }); Console.WriteLine("\nTransfer Finished"); MessageBox.Show("Transfer Complete", "TFTP Client", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void TftpClient_Disconnected() { TransferButtonDelegate tBtnDel = new TransferButtonDelegate(TransferBtnDelegateFunction); btnTest.Invoke(tBtnDel, true); Console.WriteLine("Disconnected\n"); } private void btnTest_Click(object sender, EventArgs e) { progressBarTftp.Value = 0; TransferOptions tOptions = new TransferOptions(); //tOptions.LocalFilename = "D:\\Project\\QDRP3\\Sw\\QDRP\\QDRP\\bin\\Debug\\Case\\PLBIN.bin"; tOptions.LocalFilename = "F:\\Test\\PLBIN.bin"; tOptions.RemoteFilename = "PLBIN.bin"; tOptions.Host = "10.11.9.62"; //tOptions.Action = getRadio.Checked == true ? TransferType.Get : TransferType.Put; tOptions.Action = TransferType.Get; Thread tThread = new Thread((ParameterizedThreadStart)delegate (object ScanOptions) { TransferOptions options = (TransferOptions)ScanOptions; if (options.Action == TransferType.Get) tftpClient.Get(options.LocalFilename,options.RemoteFilename,options.Host); else tftpClient.Put(options.LocalFilename, options.RemoteFilename, options.Host); }); tThread.IsBackground = true; tThread.Start(tOptions); } } }
到此这篇关于C#实现TFTP客户端的文章就介绍到这了,更多相关C# TFTP客户端内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!