Beispiel #1
0
        // 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);
            ReceiverState state = ReceiverState.RequestingFile;

            byte[]    checksum             = null;
            UInt32    fileSize             = 0;
            UInt32    numBlocks            = 0;
            UInt32    totalRequestedBlocks = 0;
            Stopwatch transferTimer        = new Stopwatch();

            // Small function to reset the transfer state
            Action ResetTransferState = new Action(() =>
            {
                state                = ReceiverState.RequestingFile;
                checksum             = null;
                fileSize             = 0;
                numBlocks            = 0;
                totalRequestedBlocks = 0;
                _blockRequestQueue.Clear();
                _blocksReceived.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 ReceiverState.RequestingFile:
                    // Create the REQF
                    RequestFilePacket REQF = new RequestFilePacket();
                    REQF.Filename = filename;

                    // Send it
                    byte[] buffer = REQF.GetBytes();
                    _client.Send(buffer, buffer.Length);

                    // Move the state to waiting for ACK
                    state = ReceiverState.WaitingForRequestFileACK;
                    break;

                case ReceiverState.WaitingForRequestFileACK:
                    // If it is an ACK and the payload is the filename, we're good
                    bool isAck = (nm == null) ? false : (nm.Packet.IsAck);
                    if (isAck)
                    {
                        AckPacket ACK = new AckPacket(nm.Packet);

                        // Make sure they respond with the filename
                        if (ACK.Message == filename)
                        {
                            // They got it, shift the state
                            state = ReceiverState.WaitingForInfo;
                            Console.WriteLine("They have the file, waiting for INFO...");
                        }
                        else
                        {
                            ResetTransferState();       // Not what we wanted, reset
                        }
                    }
                    break;

                case ReceiverState.WaitingForInfo:
                    // Verify it's file info
                    bool isInfo = (nm == null) ? false : (nm.Packet.IsInfo);
                    if (isInfo)
                    {
                        // Pull data
                        InfoPacket INFO = new InfoPacket(nm.Packet);
                        fileSize  = INFO.FileSize;
                        checksum  = INFO.Checksum;
                        numBlocks = INFO.BlockCount;

                        // Allocate some client side resources
                        Console.WriteLine("Received an INFO packet:");
                        Console.WriteLine("  Max block size: {0}", INFO.MaxBlockSize);
                        Console.WriteLine("  Num blocks: {0}", INFO.BlockCount);

                        // Send an ACK for the INFO
                        AckPacket ACK = new AckPacket();
                        ACK.Message = "INFO";
                        buffer      = ACK.GetBytes();
                        _client.Send(buffer, buffer.Length);

                        // Shift the state to ready
                        state = ReceiverState.PreparingForTransfer;
                    }
                    break;

                case ReceiverState.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 = ReceiverState.Transfering;
                    break;

                case ReceiverState.Transfering:
                    // Send a block request
                    if (_blockRequestQueue.Count > 0)
                    {
                        // Setup a request for a Block
                        UInt32             id   = _blockRequestQueue.Dequeue();
                        RequestBlockPacket REQB = new RequestBlockPacket();
                        REQB.Number = id;

                        // Send the Packet
                        buffer = REQB.GetBytes();
                        _client.Send(buffer, buffer.Length);

                        // Some handy info
                        Console.WriteLine("Sent request for Block #{0}", id);
                    }

                    // Check if we have any blocks ourselves in the queue
                    bool isSend = (nm == null) ? false : (nm.Packet.IsSend);
                    if (isSend)
                    {
                        // Get the data (and save it
                        SendPacket SEND  = new SendPacket(nm.Packet);
                        Block      block = SEND.Block;
                        _blocksReceived.Add(block.Number, block);

                        // Print some info
                        Console.WriteLine("Received Block #{0} [{1} bytes]", block.Number, block.Data.Length);
                    }

                    // Requeue any requests that we haven't received
                    if ((_blockRequestQueue.Count == 0) && (_blocksReceived.Count != numBlocks))
                    {
                        for (UInt32 id = 1; id <= numBlocks; id++)
                        {
                            if (!_blocksReceived.ContainsKey(id) && !_blockRequestQueue.Contains(id))
                            {
                                _blockRequestQueue.Enqueue(id);
                                totalRequestedBlocks++;
                            }
                        }
                    }

                    // Did we get all the block we need?  Move to the "transfer successful state."
                    if (_blocksReceived.Count == numBlocks)
                    {
                        state = ReceiverState.TransferSuccessful;
                    }
                    break;

                case ReceiverState.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 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 server 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
        }
        // Main loop of the sender
        public void Run()
        {
            // Transfer state
            SenderState state         = SenderState.WaitingForFileRequest;
            string      requestedFile = "";
            IPEndPoint  receiver      = null;

            // This is a handly 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("Received a BYE message, waiting for next client.");
                }

                // Do an action depending on the current state
                switch (state)
                {
                case SenderState.WaitingForFileRequest:
                    // Check to see that we got a file request

                    // If there was a packet, and it's a request file, send and ACK and switch the state
                    bool isRequestFile = (nm == null) ? false : nm.Packet.IsRequestFile;
                    if (isRequestFile)
                    {
                        // Prepare the ACK
                        RequestFilePacket REQF = new RequestFilePacket(nm.Packet);
                        AckPacket         ACK  = new 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 receiver
                            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 in memory
                    byte[] checksum;
                    UInt32 fileSize;
                    if (_prepareFile(requestedFile, out checksum, out fileSize))
                    {
                        // It's good, send an info Packet
                        InfoPacket INFO = new 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)
                    {
                        AckPacket ACK = new AckPacket(nm.Packet);
                        if (ACK.Message == "INFO")
                        {
                            Console.WriteLine("Starting Transfer...");
                            state = SenderState.Transfering;
                        }
                    }
                    break;

                case SenderState.Transfering:
                    // If there is a block request, send it
                    bool isRequestBlock = (nm == null) ? false : nm.Packet.IsRequestBlock;
                    if (isRequestBlock)
                    {
                        // Pull out data
                        RequestBlockPacket REQB = new RequestBlockPacket(nm.Packet);
                        Console.WriteLine("Got request for Block #{0}", REQB.Number);

                        // Create the response packet
                        Block      block = _blocks[REQB.Number];
                        SendPacket SEND  = new 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 receiver 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;
        }