// Main loop of the sender public void Run() { // Transfer state SenderState state = SenderState.WaitingForFileRequest; string requestedFile = ""; IPEndPoint receiver = null; // This is a handy little function to reset the transfer state Action ResetTransferState = new Action(() => { state = SenderState.WaitingForFileRequest; requestedFile = ""; receiver = null; blocks.Clear(); }); while (Running) { // check for some new messages checkForNetworkMessages(); NetworkMessage nm = (packetQueue.Count > 0) ? packetQueue.Dequeue() : null; // check to see if we have a BYE bool isBye = (nm == null) ? false : nm.Packet.IsBye; if (isBye) { // Set back to the original state ResetTransferState(); Console.WriteLine("Recieved a BYE message, waiting for the next client."); } // Do an action depending on the current state switch (state) { case SenderState.WaitingForFileRequest: // check to see that we got the file request // If there was a packet, and its a reqeust file, send and ACK and switch the state bool isRequestFile = (nm == null) ? false : nm.Packet.IsRequestFile; if (isRequestFile) { // Prepare the ACK Packet.RequestFilePacket REQF = new Packet.RequestFilePacket(nm.Packet); Packet.AckPacket ACK = new Packet.AckPacket(); requestedFile = REQF.Filename; // Print info Console.WriteLine("{0} has requested file file\"{1}\".", nm.Sender, requestedFile); // Check that we have the file if (transferableFiles.Contains(requestedFile)) { // mark that we have the file, save the sender as our current reciever receiver = nm.Sender; ACK.Message = requestedFile; state = SenderState.PreparingFileForTransfer; Console.WriteLine(" We have it."); } else { ResetTransferState(); } // Send the message byte[] buffer = ACK.GetBytes(); client.Send(buffer, buffer.Length, nm.Sender); } break; case SenderState.PreparingFileForTransfer: //Using the requested file, prepare it inmemory byte[] checksum; UInt32 fileSize; if (PrepareFile(requestedFile, out checksum, out fileSize)) { // it's good, send an info packet Packet.InfoPacket INFO = new Packet.InfoPacket(); INFO.Checksum = checksum; INFO.FileSize = fileSize; INFO.MaxBlockSize = MaxBlockSize; INFO.BlockCount = Convert.ToUInt32(blocks.Count); // Send it byte[] buffer = INFO.GetBytes(); client.Send(buffer, buffer.Length, receiver); // Move the state Console.WriteLine("Sending INFO, waiting for ACK..."); state = SenderState.WaitingForINfoACK; } else { ResetTransferState(); // File not good, reset the state } break; case SenderState.WaitingForINfoACK: // If it is an ACK and the payload is the filename, we're good bool isAck = (nm == null) ? false : (nm.Packet.IsAck); if (isAck) { Packet.AckPacket ACK = new Packet.AckPacket(nm.Packet); if (ACK.Message == "INFO") { Console.WriteLine("Starting Transfer..."); state = SenderState.Transferring; } } break; case SenderState.Transferring: // IF there is a block request, send it bool isRequestBlock = (nm == null) ? false : nm.Packet.IsRequestBlock; if (isRequestBlock) { // Pull out data Packet.RequestBlockPacket REQB = new Packet.RequestBlockPacket(nm.Packet); Console.WriteLine("Got request for block #{0}", REQB.Number); // Create teh response packet Block block = blocks[REQB.Number]; Packet.SendPacket SEND = new Packet.SendPacket(); SEND.Block = block; // Send it byte[] buffer = SEND.GetBytes(); client.Send(buffer, buffer.Length, nm.Sender); Console.WriteLine("Sent Block #{0} [{1} bytes]", block.Number, block.Data.Length); } break; } Thread.Sleep(1); } // If there was a reciever set, that means we need to notify it to shutdown if (receiver != null) { Packet BYE = new Packet(Packet.Bye); byte[] buffer = BYE.GetBytes(); client.Send(buffer, buffer.Length, receiver); } state = SenderState.NotRunning; }
// Tries to grab a file and download it to our local machine public void GetFile(string filename) { // Init the get file state Console.WriteLine("requesting file: {0}", filename); RecieverState state = RecieverState.RequestingFile; byte[] checksum = null; UInt32 fileSize = 0; UInt32 numBlocks = 0; UInt32 totalRequestedBlocks = 0; Stopwatch transferTimer = new Stopwatch(); // small function to reset the tranfer state Action ResetTransferState = new Action(() => { state = RecieverState.RequestingFile; checksum = null; fileSize = 0; numBlocks = 0; totalRequestedBlocks = 0; blockRequestQueue.Clear(); blocksRecieved.Clear(); transferTimer.Reset(); }); // Main loop running = true; bool senderQuit = false; bool wasRunning = running; while (running) { // check for some new packets (if there are some) CheckForNetworkMessages(); NetworkMessage nm = (packetQueue.Count > 0) ? packetQueue.Dequeue() : null; // In case the sender is shutdown, quit bool isBye = (nm == null) ? false : nm.Packet.IsBye; if (isBye) { senderQuit = true; } // the state switch (state) { case RecieverState.RequestingFile: // create the REQF Packet.RequestFilePacket REQF = new Packet.RequestFilePacket(); REQF.Filename = filename; // Send it byte[] buffer = REQF.GetBytes(); client.Send(buffer, buffer.Length); // Move the state to waiting for ACK state = RecieverState.WaitingForRequestFileACK; break; case RecieverState.WaitingForRequestFileACK: // If there is an ACK and the payload is the filename, we're good bool isAck = (nm == null) ? false : (nm.Packet.IsAck); if (isAck) { Packet.AckPacket ACK = new Packet.AckPacket(); // Make sure they respond with the filename if (ACK.Message == filename) { // they got it, shift the state state = RecieverState.WaitingForInfo; Console.WriteLine("They have the file, waiting for INFO..."); } else { ResetTransferState(); // not what we wanted, reset } } break; case RecieverState.WaitingForInfo: // verify its file info bool isInfo = (nm == null) ? false : (nm.Packet.IsInfo); if (isInfo) { // pull data Packet.InfoPacket INFO = new Packet.InfoPacket(nm.Packet); fileSize = INFO.FileSize; checksum = INFO.Checksum; numBlocks = INFO.BlockCount; // allocate some client side resources Console.WriteLine("Recieved an INFO packet:"); Console.WriteLine(" Max block size: {0}", INFO.MaxBlockSize); Console.WriteLine(" Nmum blocks: {0}", INFO.BlockCount); // Send an ACK for the INFO Packet.AckPacket ACK = new Packet.AckPacket(); ACK.Message = "INFO"; buffer = ACK.GetBytes(); client.Send(buffer, buffer.Length); // Shift the state to ready state = RecieverState.PreparingForTransfer; } break; case RecieverState.PreparingForTransfer: // Prepare the request queue for (UInt32 id = 1; id <= numBlocks; id++) { blockRequestQueue.Enqueue(id); } totalRequestedBlocks += numBlocks; // Shift the state Console.WriteLine("Starting Transfer..."); transferTimer.Start(); state = RecieverState.Transfering; break; case RecieverState.Transfering: // send a block request if (blockRequestQueue.Count > 0) { // setup a request for a block UInt32 id = blockRequestQueue.Dequeue(); Packet.RequestBlockPacket REQB = new Packet.RequestBlockPacket(); REQB.Number = id; // send the packet buffer = REQB.GetBytes(); client.Send(buffer, buffer.Length); // some handy info Console.WriteLine("Sent reqeust for block#{0}, id"); } // check to see if we have nay blocks oruselves in the queue bool isSend = (nm == null) ? false : (nm.Packet.IsSend); if (isSend) { // Get the data (and save it) Packet.SendPacket SEND = new Packet.SendPacket(); Block block = SEND.Block; blocksRecieved.Add(block.Number, block); // print some info Console.WriteLine("Recieved Block #{0} [{1} bytes]", block.Number, block.Data.Length); } // Requeue any requests that we haven't recieved if ((blockRequestQueue.Count == 0) && (blocksRecieved.Count != numBlocks)) { for (UInt32 id = 1; id <= numBlocks; id++) { if (!blocksRecieved.ContainsKey(id) && blockRequestQueue.Contains(id)) { blockRequestQueue.Enqueue(id); totalRequestedBlocks++; } } } // Did we get all the blocks we need? Move to the "transfer successful state." if (blocksRecieved.Count == numBlocks) { state = RecieverState.TransferSuccessful; } break; case RecieverState.TransferSuccessful: transferTimer.Stop(); // things were good, send a BYE message Packet BYE = new Packet(Packet.Bye); buffer = BYE.GetBytes(); client.Send(buffer, buffer.Length); Console.WriteLine("Transfer successful; it took {0:0.000}s with a success ratio of {1:0.000}.", transferTimer.Elapsed.TotalSeconds, (double)numBlocks / (double)totalRequestedBlocks); Console.WriteLine("Decompressing the Blocks..."); //Reconstruct the data if (SaveBlocksToFile(filename, checksum, fileSize)) { Console.WriteLine("Saved file as {0}", filename); } else { Console.WriteLine("There was some trouble in saving the Blocks to {0}.", filename); } // And we're done here running = false; break; } // Sleep Thread.Sleep(1); // Check for the shutdown running &= !shutdownRequested; running &= !senderQuit; } // Send a BYE message if the user wanted to cancel if (shutdownRequested && wasRunning) { Console.WriteLine("User canceled transfer."); Packet BYE = new Packet(Packet.Bye); byte[] buffer = BYE.GetBytes(); client.Send(buffer, buffer.Length); } // IF the sever told us to shutdown if (senderQuit && wasRunning) { Console.WriteLine("The sender quit on us, canceling the transfer."); } ResetTransferState(); // This also cleans up collections shutdownRequested = false; // In case we shut down one download, but want to start a new one }