public void Write(IPEndPoint server, string fileName, string inputPath, CancellationToken cancellationToken) { Console.WriteLine("Writing local file '{0}' to file '{1}' on server {2}", inputPath, fileName, server); // Read the file from the local file system. byte[] file = File.ReadAllBytes(inputPath); // Create a connection with the server. IPEndPoint receivedFromEP = new IPEndPoint(IPAddress.Any, 0); using (RemoteEndPointReservation clientReservation = new RemoteEndPointReservation(server)) using (TftpConnection connection = new TftpConnection()) { RemoteEndPointReservation remoteEPReservation = null; try { // Read the file contents to the remote endpoint in chunks. ushort currentBlockNumber = 0; int currentFileIndex = 0; bool done = false; while (!done) { Func <ITftpPacket, bool> receiveFilter = (ITftpPacket packet) => { if (packet.Op == TftpOperation.Ack) { AckPacket ack = (AckPacket)packet; if (ack.BlockNumber == currentBlockNumber) { Console.WriteLine("ACK received for block {0}", ack.BlockNumber); return(true); } else { Console.WriteLine("Incorrect ACK block number {0}", ack.BlockNumber); } } else { Console.WriteLine("Incorrect operation. Expected ACK."); } return(false); }; ITftpPacket sendPacket; // To initialize the transfer, send WRQ and wait for an ACK of block 0. if (currentBlockNumber == 0) { Console.WriteLine("Sending WRQ"); // NOTE: the server only supports octet mode by design. sendPacket = new WrqPacket(fileName, TftpMode.octet); IPEndPoint remoteEP; connection.SendAndWaitForResponse(sendPacket, server, receiveFilter, out remoteEP, cancellationToken); remoteEPReservation = new RemoteEndPointReservation(remoteEP); connection.Connect(remoteEP); } else { // Extract the current block from the file contents and put it in a DATA packet. Console.WriteLine("Sending block {0}", currentBlockNumber); int remainingBytes = file.Length - currentFileIndex; int blockSize; if (remainingBytes < DataPacket.MaxBlockSize) { blockSize = remainingBytes; done = true; } else { blockSize = DataPacket.MaxBlockSize; } byte[] block = new byte[blockSize]; Buffer.BlockCopy(file, currentFileIndex, block, 0, blockSize); currentFileIndex += block.Length; sendPacket = new DataPacket(blockNumber: currentBlockNumber, data: block); // Send the DATA packet and wait for the corresponding ACK. connection.SendAndWaitForResponse(sendPacket, receiveFilter, cancellationToken); } // Move to the next block. currentBlockNumber++; } } finally { if (remoteEPReservation != null) { remoteEPReservation.Dispose(); } } } }
// Send the file named fileName to remoteEP. private void ExecuteRead(string fileName, IPEndPoint clientEP, CancellationToken cancellationToken) { Console.WriteLine("RRQ for file '{0}' received from client '{1}'", fileName, clientEP); // Reserve a local port and create a connection with the client. IPEndPoint receivedFromEP = new IPEndPoint(IPAddress.Any, 0); using (RemoteEndPointReservation clientReservation = new RemoteEndPointReservation(clientEP)) using (TftpConnection connection = new TftpConnection()) { connection.Connect(clientEP); // Check if the file exists. If so, get the file contents. bool fileFound; byte[] file = null; lock (_files) { fileFound = _files.ContainsKey(fileName); if (fileFound) { // WARNING: files are read-only and do not support deletion in the protocol spec as of 6/9/14, so we do not need to make a copy of the file data. file = _files[fileName]; } } // If the file was not found, send an error to the client and terminate the connection. if (!fileFound) { string message = string.Format("Could not find file '{0}'", fileName); Console.WriteLine(message); connection.Send(new ErrorPacket( error: TftpError.FileNotFound, message: message)); return; } // Read the file contents to the remote endpoint in chunks. ushort currentBlockNumber = 1; int currentFileIndex = 0; bool done = false; while (!done) { // Extract the current block from the file contents and put it in a DATA packet. int remainingBytes = file.Length - currentFileIndex; int blockSize; if (remainingBytes < DataPacket.MaxBlockSize) { blockSize = remainingBytes; done = true; } else { blockSize = DataPacket.MaxBlockSize; } byte[] block = new byte[blockSize]; Buffer.BlockCopy(file, currentFileIndex, block, 0, blockSize); ITftpPacket sendPacket = new DataPacket(blockNumber: currentBlockNumber, data: block); // Send the DATA packet and wait for the corresponding ACK. Func <ITftpPacket, bool> receiveFilter = (ITftpPacket packet) => { if (packet.Op == TftpOperation.Ack) { AckPacket ack = (AckPacket)packet; if (ack.BlockNumber == currentBlockNumber) { Console.WriteLine("ACK received for block {0}", ack.BlockNumber); return(true); } else { Console.WriteLine("Incorrect ACK block number {0}", ack.BlockNumber); } } else { Console.WriteLine("Incorrect operation. Expected ACK."); } return(false); }; Console.WriteLine("Sending block {0}", currentBlockNumber); connection.SendAndWaitForResponse( sendPacket, receiveFilter, cancellationToken); // Move to the next block. currentBlockNumber++; currentFileIndex += block.Length; } } }
public void Read(IPEndPoint server, string fileName, string outputPath, CancellationToken cancellationToken) { Console.WriteLine("Reading to local file '{0}' from file '{1}' on server {2}", outputPath, fileName, server); using (FileStream outputFileStream = new FileStream(outputPath, FileMode.CreateNew, FileAccess.Write, FileShare.Read)) using (TftpConnection connection = new TftpConnection()) { RemoteEndPointReservation remoteEPReservation = null; try { bool initialized = false; ushort currentBlockNumber = 1; DataPacket currentDataPacket; AckPacket ackPacket = null; while (true) { Func <ITftpPacket, bool> receiveFilter = (ITftpPacket packet) => { if (packet.Op == TftpOperation.Data) { DataPacket d = (DataPacket)packet; if (d.BlockNumber == currentBlockNumber) { return(true); } else { Console.WriteLine("Incorrect DATA block number."); } } else { Console.WriteLine("Incorrect operation. Expected DATA."); } return(false); }; // To initialize the transfer, send RRQ and wait for the first data block. if (!initialized) { Console.WriteLine("Sending RRQ"); // NOTE: the server only supports octet mode by design. RrqPacket requestPacket = new RrqPacket(fileName, TftpMode.octet); IPEndPoint remoteEP; currentDataPacket = (DataPacket)connection.SendAndWaitForResponse(requestPacket, server, receiveFilter, out remoteEP, cancellationToken); remoteEPReservation = new RemoteEndPointReservation(remoteEP); connection.Connect(remoteEP); initialized = true; } else { Console.WriteLine("Sending ack for block number {0}", ackPacket.BlockNumber); currentDataPacket = (DataPacket)connection.SendAndWaitForResponse(ackPacket, receiveFilter, cancellationToken); } Console.WriteLine("DATA packet containing block {0} received", currentDataPacket.BlockNumber); outputFileStream.Write(currentDataPacket.Data, 0, currentDataPacket.Data.Length); ackPacket = new AckPacket(currentBlockNumber); // When the received DATA packet is less than the maximum length, send a final ack and terminate. if (currentDataPacket.Data.Length < DataPacket.MaxBlockSize) { connection.Send(ackPacket); break; } ++currentBlockNumber; } } finally { if (remoteEPReservation != null) { remoteEPReservation.Dispose(); } } } }
// Write the file named fileName using data from remoteEP. private void ExecuteWrite(string fileName, IPEndPoint clientEP, CancellationToken cancellationToken) { Console.WriteLine("WRQ for file '{0}' received from client {1}", fileName, clientEP); // Reserve a local port and create a connection with the client. using (MemoryStream fileInMemory = new MemoryStream()) using (RemoteEndPointReservation clientReservation = new RemoteEndPointReservation(clientEP)) using (TftpConnection connection = new TftpConnection()) { connection.Connect(clientEP); ushort currentBlockNumber = 1; DataPacket currentDataPacket; AckPacket ackPacket = new AckPacket(0); while (true) { Func <ITftpPacket, bool> receiveFilter = (ITftpPacket packet) => { if (packet.Op == TftpOperation.Data) { DataPacket d = (DataPacket)packet; if (d.BlockNumber == currentBlockNumber) { return(true); } else { Console.WriteLine("Incorrect DATA block number."); } } else { Console.WriteLine("Incorrect operation. Expected DATA."); } return(false); }; Console.WriteLine("Sending ack for block number {0}", ackPacket.BlockNumber); currentDataPacket = (DataPacket)connection.SendAndWaitForResponse(ackPacket, receiveFilter, cancellationToken); Console.WriteLine("DATA packet containing block {0} received", currentDataPacket.BlockNumber); fileInMemory.Write(currentDataPacket.Data, 0, currentDataPacket.Data.Length); ackPacket = new AckPacket(currentBlockNumber); // When the received DATA packet is less than the maximum length, that is the final DATA packet. if (currentDataPacket.Data.Length < DataPacket.MaxBlockSize) { lock (_files) { // If the file already exists, send an error. if (_files.ContainsKey(fileName)) { string message = string.Format("File {0} already exists.", fileName); Console.WriteLine(message); connection.Send(new ErrorPacket( error: TftpError.FileAlreadyExists, message: message)); } // Otherwise, commit the received bytes to the file dictionary and send the final ACK. else { _files[fileName] = fileInMemory.ToArray(); connection.Send(ackPacket); } } break; } ++currentBlockNumber; } } }