// 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;
        }
示例#2
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);
            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
        }