/// <summary>
        /// Remove a connection specific shutdown delegate.
        /// </summary>
        /// <param name="handlerToRemove">The delegate to remove for shutdown events</param>
        public void RemoveShutdownHandler(NetworkComms.ConnectionEstablishShutdownDelegate handlerToRemove)
        {
            lock (_syncRoot)
            {
                ConnectionSpecificShutdownDelegate -= handlerToRemove;
                if (NetworkComms.LoggingEnabled)
                {
                    NetworkComms.Logger.Debug("Removed ConnectionSpecificShutdownDelegate to connection with " + ConnectionInfo);
                }

                if (ConnectionSpecificShutdownDelegate == null)
                {
                    if (NetworkComms.LoggingEnabled)
                    {
                        NetworkComms.Logger.Info("No handlers remain for ConnectionSpecificShutdownDelegate with " + ConnectionInfo);
                    }
                }
                else
                {
                    if (NetworkComms.LoggingEnabled)
                    {
                        NetworkComms.Logger.Info("Handlers remain for ConnectionSpecificShutdownDelegate with " + ConnectionInfo);
                    }
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Send an object using the provided <see cref="SendReceiveOptions"/> and wait for a returned object using provided <see cref="SendReceiveOptions"/>.
        /// </summary>
        /// <typeparam name="returnObjectType">The type of return object</typeparam>
        /// <param name="sendingPacketTypeStr">The sending packet type</param>
        /// <param name="expectedReturnPacketTypeStr">The packet type which will be used for the reply</param>
        /// <param name="returnPacketTimeOutMilliSeconds">A timeout in milliseconds after which if not reply is received will throw an ExpectedReturnTimeoutException.</param>
        /// <param name="sendObject">The object to send</param>
        /// <param name="sendOptions">SendReceiveOptions to use when sending</param>
        /// <param name="receiveOptions">SendReceiveOptions used when receiving the return object</param>
        /// <param name="sentPacketSequenceNumber">The sequence number of the packet sent</param>
        /// <returns>The requested return object</returns>
        public returnObjectType SendReceiveObject <returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, object sendObject, SendReceiveOptions sendOptions, SendReceiveOptions receiveOptions, out long sentPacketSequenceNumber)
        {
            returnObjectType returnObject = default(returnObjectType);

            bool           remotePeerDisconnectedDuringWait = false;
            AutoResetEvent returnWaitSignal = new AutoResetEvent(false);

            #region SendReceiveDelegate
            NetworkComms.PacketHandlerCallBackDelegate <returnObjectType> SendReceiveDelegate = (packetHeader, sourceConnection, incomingObject) =>
            {
                returnObject = incomingObject;
                returnWaitSignal.Set();
            };

            //We use the following delegate to quickly force a response timeout if the remote end disconnects
            NetworkComms.ConnectionEstablishShutdownDelegate SendReceiveShutDownDelegate = (sourceConnection) =>
            {
                remotePeerDisconnectedDuringWait = true;
                returnObject = default(returnObjectType);
                returnWaitSignal.Set();
            };
            #endregion

            if (sendOptions == null)
            {
                sendOptions = ConnectionDefaultSendReceiveOptions;
            }
            if (receiveOptions == null)
            {
                receiveOptions = ConnectionDefaultSendReceiveOptions;
            }

            AppendShutdownHandler(SendReceiveShutDownDelegate);
            AppendIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate, receiveOptions);

            using (Packet sendPacket = new Packet(sendingPacketTypeStr, expectedReturnPacketTypeStr, sendObject, sendOptions))
                SendPacket(sendPacket, out sentPacketSequenceNumber);

            //We wait for the return data here
            if (!returnWaitSignal.WaitOne(returnPacketTimeOutMilliSeconds))
            {
                RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate);
                throw new ExpectedReturnTimeoutException("Timeout occurred after " + returnPacketTimeOutMilliSeconds.ToString() + "ms waiting for response packet of type '" + expectedReturnPacketTypeStr + "'.");
            }

            RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate);
            RemoveShutdownHandler(SendReceiveShutDownDelegate);

            if (remotePeerDisconnectedDuringWait)
            {
                throw new ExpectedReturnTimeoutException("Remote end closed connection before data was successfully returned.");
            }
            else
            {
                return(returnObject);
            }
        }
        public override void InitializePacket()
        {
            closeHandler = new NetworkComms.ConnectionEstablishShutdownDelegate((Connection connection) =>
            {
                mainWindow.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
                {
                    mainWindow.ConnectionClosedLogOut();
                }));
            });

            Open();
        }
        /// <summary>
        /// Add a connection specific shutdown delegate
        /// </summary>
        /// <param name="handlerToAppend">The delegate to call when a connection is shutdown</param>
        public void AppendShutdownHandler(NetworkComms.ConnectionEstablishShutdownDelegate handlerToAppend)
        {
            lock (_syncRoot)
            {
                if (ConnectionSpecificShutdownDelegate == null)
                {
                    ConnectionSpecificShutdownDelegate = handlerToAppend;
                }
                else
                {
                    ConnectionSpecificShutdownDelegate += handlerToAppend;
                }

                if (NetworkComms.LoggingEnabled)
                {
                    NetworkComms.Logger.Debug("Added a connection specific shutdown delegate to connection with " + ConnectionInfo);
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Send the provided packet to the remoteEndPoint. Waits for receive confirmation if required.
        /// </summary>
        /// <param name="packet">The packet to send</param>
        /// <param name="packetSequenceNumber">The sequence number of the packet sent</param>
        internal void SendPacket(Packet packet, out long packetSequenceNumber)
        {
            if (NetworkComms.LoggingEnabled)
            {
                string packetDataMD5 = "";
                if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.CheckSumHash))
                {
                    packetDataMD5 = packet.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash);
                }

                NetworkComms.Logger.Trace("Entering packet send of '" + packet.PacketHeader.PacketType + "' packetType to " + ConnectionInfo + (packetDataMD5 == "" ? "" : ". PacketCheckSum=" + packetDataMD5));
            }

            //Multiple threads may try to send packets at the same time so wait one at a time here
            lock (sendLocker)
            {
                //We don't allow sends on a closed connection
                if (ConnectionInfo.ConnectionState == ConnectionState.Shutdown)
                {
                    throw new CommunicationException("Attempting to send packet on connection which has been closed or is currently closing.");
                }

                //Set packet sequence number inside sendLocker
                //Increment the global counter as well to ensure future connections with the same host can not create duplicates
                Interlocked.Increment(ref NetworkComms.totalPacketSendCount);
                packetSequenceNumber = packetSequenceCounter++;
                packet.PacketHeader.SetOption(PacketHeaderLongItems.PacketSequenceNumber, packetSequenceNumber);

                string         confirmationCheckSum             = "";
                AutoResetEvent confirmationWaitSignal           = new AutoResetEvent(false);
                bool           remotePeerDisconnectedDuringWait = false;

                #region Delegates
                //Specify a delegate we may use if we require receive confirmation
                NetworkComms.PacketHandlerCallBackDelegate <string> confirmationDelegate = (packetHeader, connectionInfo, incomingString) =>
                {
                    //if (connectionInfo.NetworkIdentifier == this.ConnectionInfo.NetworkIdentifier && connectionInfo.RemoteEndPoint == this.ConnectionInfo.RemoteEndPoint)
                    //{
                    confirmationCheckSum = incomingString;
                    confirmationWaitSignal.Set();
                    //}
                };

                //We use the following delegate to quickly force a response timeout if the remote end disconnects during a send/wait
                NetworkComms.ConnectionEstablishShutdownDelegate ConfirmationShutDownDelegate = (connectionInfo) =>
                {
                    //if (connectionInfo.NetworkIdentifier == this.ConnectionInfo.NetworkIdentifier && connectionInfo.RemoteEndPoint == this.ConnectionInfo.RemoteEndPoint)
                    //{
                    remotePeerDisconnectedDuringWait = true;
                    confirmationWaitSignal.Set();
                    //}
                };
                #endregion

                try
                {
                    #region Prepare For Confirmation and Possible Validation
                    //Add the confirmation handler if required
                    if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired))
                    {
                        AppendIncomingPacketHandler(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), confirmationDelegate, NetworkComms.InternalFixedSendReceiveOptions);
                        AppendShutdownHandler(ConfirmationShutDownDelegate);
                    }

                    //If this packet is not a checkSumFailResend
                    if (NetworkComms.EnablePacketCheckSumValidation && packet.PacketHeader.PacketType != Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.CheckSumFailResend))
                    {
                        //We only want to keep packets when they are under some provided theshold
                        //otherwise this becomes a quick 'memory leak'
                        if (packet.PacketData.Length < NetworkComms.CheckSumMismatchSentPacketCacheMaxByteLimit)
                        {
                            lock (sentPacketsLocker)
                            {
                                var hash = packet.PacketHeader.GetOption(PacketHeaderStringItems.CheckSumHash);

                                if (!sentPackets.ContainsKey(hash))
                                {
                                    sentPackets.Add(hash, new SentPacket(packet));
                                }
                            }
                        }
                    }
                    #endregion

                    SendPacketSpecific(packet);

                    #region SentPackets Cleanup
                    //If sent packets is greater than 40 we delete anything older than a minute
                    lock (sentPacketsLocker)
                    {
                        if ((DateTime.Now - NetworkComms.LastSentPacketCacheCleanup).TotalMinutes > NetworkComms.MinimumSentPacketCacheTimeMinutes / 2)
                        {
                            Dictionary <string, SentPacket> newSentPackets = new Dictionary <string, SentPacket>();
                            DateTime thresholdTime = DateTime.Now.AddMinutes(-NetworkComms.MinimumSentPacketCacheTimeMinutes);
                            foreach (var storedPacket in sentPackets)
                            {
                                if (storedPacket.Value.SentPacketCreationTime >= thresholdTime)
                                {
                                    newSentPackets.Add(storedPacket.Key, storedPacket.Value);
                                }
                            }

                            sentPackets = newSentPackets;
                            NetworkComms.LastSentPacketCacheCleanup = DateTime.Now;
                        }
                    }
                    #endregion

                    #region Wait For Confirmation If Required
                    //If we required receive confirmation we now wait for that confirmation
                    if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired))
                    {
                        if (NetworkComms.LoggingEnabled)
                        {
                            NetworkComms.Logger.Trace(" ... waiting for receive confirmation packet.");
                        }

                        if (!(confirmationWaitSignal.WaitOne(NetworkComms.PacketConfirmationTimeoutMS)))
                        {
                            throw new ConfirmationTimeoutException("Confirmation packet timeout.");
                        }

                        if (remotePeerDisconnectedDuringWait)
                        {
                            throw new ConfirmationTimeoutException("Remote end closed connection before confirmation packet was returned.");
                        }
                        else
                        {
                            if (NetworkComms.LoggingEnabled)
                            {
                                NetworkComms.Logger.Trace(" ... confirmation packet received.");
                            }
                        }
                    }
                    #endregion

                    //Update the traffic time as late as possible incase there is a problem
                    ConnectionInfo.UpdateLastTrafficTime();
                }
                catch (ConfirmationTimeoutException)
                {
                    //Confirmation timeout there is no need to close the connection as this
                    //does not neccessarily mean there is a conneciton problem
                    throw;
                }
                catch (CommunicationException)
                {
                    //We close the connection due to communication exceptions
                    CloseConnection(true, 47);
                    throw;
                }
                catch (TimeoutException ex)
                {
                    //We close the connection due to communication exceptions
                    if (NetworkComms.LoggingEnabled)
                    {
                        NetworkComms.Logger.Warn("Timeout exception for connection " + this.ConnectionInfo + (ex.Message != null ? ". " + ex.Message : "."));
                    }

                    CloseConnection(true, 48);
                    throw new ConnectionSendTimeoutException(ex.ToString());
                }
                catch (Exception ex)
                {
                    //We close the connection due to communication exceptions
                    CloseConnection(true, 49);
                    throw new CommunicationException(ex.ToString());
                }
                finally
                {
                    if (packet.PacketHeader.ContainsOption(PacketHeaderStringItems.ReceiveConfirmationRequired))
                    {
                        //Cleanup our delegates
                        RemoveIncomingPacketHandler(Enum.GetName(typeof(ReservedPacketType), ReservedPacketType.Confirmation), confirmationDelegate);
                        RemoveShutdownHandler(ConfirmationShutDownDelegate);
                    }
                }
            }

            if (NetworkComms.LoggingEnabled)
            {
                NetworkComms.Logger.Trace("Completed packet send of '" + packet.PacketHeader.PacketType + "' packetType to " + ConnectionInfo);
            }
        }
        /// <summary>
        /// Send an object using the provided <see cref="SendReceiveOptions"/> and wait for a returned object using provided
        /// <see cref="SendReceiveOptions"/>.
        /// </summary>
        /// <typeparam name="sendObjectType">The sending object type, i.e. string, int[], etc</typeparam>
        /// <typeparam name="returnObjectType">The type of return object</typeparam>
        /// <param name="sendingPacketTypeStr">The sending packet type</param>
        /// <param name="expectedReturnPacketTypeStr">The packet type which will be used for the reply</param>
        /// <param name="returnPacketTimeOutMilliSeconds">A timeout in milliseconds after which if not reply is received will
        /// throw an ExpectedReturnTimeoutException.</param>
        /// <param name="sendObject">The object to send</param>
        /// <param name="sendOptions">SendReceiveOptions to use when sending</param>
        /// <param name="receiveOptions">SendReceiveOptions used when receiving the return object</param>
        /// <param name="sentPacketSequenceNumber">The sequence number of the packet sent</param>
        /// <returns>The requested return object</returns>
        public returnObjectType SendReceiveObject <sendObjectType, returnObjectType>(string sendingPacketTypeStr, string expectedReturnPacketTypeStr, int returnPacketTimeOutMilliSeconds, sendObjectType sendObject, SendReceiveOptions sendOptions, SendReceiveOptions receiveOptions, out long sentPacketSequenceNumber)
        {
            if (sendingPacketTypeStr == expectedReturnPacketTypeStr)
            {
                throw new ArgumentException("The provided sendingPacketTypeStr and expectedReturnPacketTypeStr parameters must be different.");
            }

            returnObjectType returnObject = default(returnObjectType);

            bool           remotePeerDisconnectedDuringWait = false;
            AutoResetEvent returnWaitSignal = new AutoResetEvent(false);

            #region SendReceiveDelegate
            NetworkComms.PacketHandlerCallBackDelegate <returnObjectType> SendReceiveDelegate = (packetHeader, sourceConnection, incomingObject) =>
            {
                returnObject = incomingObject;
                returnWaitSignal.Set();
            };

            //We use the following delegate to quickly force a response timeout if the remote end disconnects
            NetworkComms.ConnectionEstablishShutdownDelegate SendReceiveShutDownDelegate = (sourceConnection) =>
            {
                remotePeerDisconnectedDuringWait = true;
                returnObject = default(returnObjectType);
                returnWaitSignal.Set();
            };
            #endregion

            if (sendOptions == null)
            {
                sendOptions = ConnectionDefaultSendReceiveOptions;
            }
            if (receiveOptions == null)
            {
                receiveOptions = ConnectionDefaultSendReceiveOptions;
            }

            AppendShutdownHandler(SendReceiveShutDownDelegate);
            AppendIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate, receiveOptions);

            //Check to see if we already have a packet
            Packet sendObjectAsPacket = sendObject as Packet;
            if (sendObjectAsPacket == null)
            {
                using (Packet sendPacket = new Packet(sendingPacketTypeStr, expectedReturnPacketTypeStr, sendObject, sendOptions))
                    SendPacket <sendObjectType>(sendPacket, out sentPacketSequenceNumber);
            }
            else
            {
                if (sendObjectAsPacket.PacketHeader.PacketType != sendingPacketTypeStr)
                {
                    throw new ArgumentException("Unable to send object of type Packet if the PacketHeader.PacketType and sendingPacketType do not match.");
                }

                SendPacket <sendObjectType>(sendObjectAsPacket, out sentPacketSequenceNumber);
            }

            //We wait for the return data here
#if NET2
            if (!returnWaitSignal.WaitOne(returnPacketTimeOutMilliSeconds, false))
#else
            if (!returnWaitSignal.WaitOne(returnPacketTimeOutMilliSeconds))
#endif
            {
                RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate);
                throw new ExpectedReturnTimeoutException("Timeout occurred after " + returnPacketTimeOutMilliSeconds.ToString() + "ms waiting for response packet of type '" + expectedReturnPacketTypeStr + "'.");
            }

            RemoveIncomingPacketHandler(expectedReturnPacketTypeStr, SendReceiveDelegate);
            RemoveShutdownHandler(SendReceiveShutDownDelegate);

            if (remotePeerDisconnectedDuringWait)
            {
                throw new ConnectionShutdownException("Remote end closed connection before data was successfully returned.");
            }
            else
            {
                return(returnObject);
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Run example
        /// </summary>
        public static void RunExample()
        {
            //Select mode
            Console.WriteLine("SpeedTest Example ...\n");

            Console.WriteLine("Please select host or peer mode:");
            Console.WriteLine("1 - Host Mode (Catches Data)");
            Console.WriteLine("2 - Peer Mode (Sends Data)");

            //Read in user choice
            if (Console.ReadKey(true).Key == ConsoleKey.D1)
            {
                hostMode = true;
            }
            else
            {
                hostMode = false;
            }

            if (hostMode)
            {
                //Prepare DFS in host mode
                #region ServerMode
                Console.WriteLine("\n ... host mode selected.");

                NetworkComms.ConnectionEstablishShutdownDelegate clientEstablishDelegate = (connection) =>
                {
                    Console.WriteLine("Client " + connection.ConnectionInfo + " connected.");
                };

                NetworkComms.ConnectionEstablishShutdownDelegate clientShutdownDelegate = (connection) =>
                {
                    Console.WriteLine("Client " + connection.ConnectionInfo + " disconnected.");
                };

                NetworkComms.PacketHandlerCallBackDelegate <byte[]> IncomingDataDelegate = (packetHeader, connection, incomingObject) =>
                {
                    Console.WriteLine("Speed bytes received from " + connection.ConnectionInfo + ".");
                };

                NetworkComms.AppendGlobalConnectionEstablishHandler(clientEstablishDelegate);
                NetworkComms.AppendGlobalConnectionCloseHandler(clientShutdownDelegate);
                NetworkComms.AppendGlobalIncomingPacketHandler("SpeedData", IncomingDataDelegate);

                //Start listening for TCP connections
                //We want to select a random port on all available adaptors so provide
                //an IPEndPoint using IPAddress.Any and port 0.
                Connection.StartListening(ConnectionType.TCP, new IPEndPoint(IPAddress.Any, 0));

                Console.WriteLine("\nListening for incoming connections on:");
                foreach (IPEndPoint localEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP))
                {
                    Console.WriteLine("{0}:{1}", localEndPoint.Address, localEndPoint.Port);
                }

                Console.WriteLine("\nIdentifier - {0}", NetworkComms.NetworkIdentifier);
                Console.WriteLine("\nPress 'q' to close host.\n");

                while (true)
                {
                    ConsoleKeyInfo pressedKey = Console.ReadKey(true);
                    if (pressedKey.Modifiers != ConsoleModifiers.Control && pressedKey.Key == ConsoleKey.Q)
                    {
                        Console.WriteLine("Closing host.");
                        break;
                    }
                }
                #endregion
            }
            else if (!hostMode)
            {
                //Prepare DFS in peer mode
                #region PeerMode
                Console.WriteLine("\n ... peer mode selected.");

                Console.WriteLine("\nPlease enter how large the test data packet should be in MB and press return (larger is more accurate), e.g. 1024:");
                int numberMegsToCreate = int.Parse(Console.ReadLine());

                //Fill a byte[] with random data
                DateTime startTime      = DateTime.Now;
                Random   randGen        = new Random();
                byte[]   someRandomData = new byte[numberMegsToCreate * 1024 * 1024];
                randGen.NextBytes(someRandomData);

                Console.WriteLine("\nTest speed data created. Using {0}MB.\n", numberMegsToCreate);

                NetworkComms.PacketConfirmationTimeoutMS = 20000;
                ConnectionInfo serverConnectionInfo = ExampleHelper.GetServerDetails();

                Console.WriteLine("\nIdentifier - {0}", NetworkComms.NetworkIdentifier);

                SendReceiveOptions nullCompressionSRO = new SendReceiveOptions <NullSerializer>();

                //Add options which will require receive confirmations and also include packet construction time
                //in the packet header.
                nullCompressionSRO.Options.Add("ReceiveConfirmationRequired", "");
                nullCompressionSRO.Options.Add("IncludePacketConstructionTime", "");

                TCPConnection serverConnection = TCPConnection.GetConnection(serverConnectionInfo);
                Stopwatch     timer            = new Stopwatch();

                while (true)
                {
                    timer.Reset();
                    timer.Start();
                    serverConnection.SendObject("SpeedData", someRandomData, nullCompressionSRO);
                    timer.Stop();
                    Console.WriteLine("SpeedData sent successfully with receive confirmation in {0}secs. Corresponds to {1}MB/s", (timer.ElapsedMilliseconds / 1000.0).ToString("0.00"), (numberMegsToCreate / (timer.ElapsedMilliseconds / 1000.0)).ToString("0.00"));
                }
                #endregion
            }

            NetworkComms.Shutdown();
        }
Exemplo n.º 8
0
        /// <summary>
        /// The distributed file system (DFS) allows for the high performance distribution of large files
        /// within a cluster of peers. This sytem replicates the behaviour the bitTorrent protocol by using
        /// NetworkCommsDotNet. This example demonstrates the DFS in action.
        /// </summary>
        public static void RunExample()
        {
            //Select launch mode
            Console.WriteLine("Launching DFS system ...\n");
            Console.WriteLine("Please select host or peer mode:");
            Console.WriteLine("1 - Host Mode (Original source of data)");
            Console.WriteLine("2 - Peer Mode (Builds data and then acts as subhost)");

            //Read in user choice
            //if (Console.ReadKey(true).Key == ConsoleKey.D1) hostMode = true;
            //else hostMode = false;
            hostMode = false;

            if (hostMode)
            {
                //Prepare DFS in host mode
                #region ServerMode
                Console.WriteLine("\n ... host mode selected.");
                Console.WriteLine("\nPlease enter how large the test data packet should be in MB and press return, e.g. 50:");
                int numberMegsToCreate = int.Parse(Console.ReadLine());

                //Fill a byte[] with random data
                DateTime startTime      = DateTime.Now;
                Random   randGen        = new Random();
                byte[]   someRandomData = new byte[numberMegsToCreate * 1024 * 1024];
                randGen.NextBytes(someRandomData);

                Console.WriteLine("\n ... successfully created a {0}MB test packet.", ((double)someRandomData.Length / (1024.0 * 1024.0)).ToString("0.###"));

                object            listLocker       = new object();
                List <IPEndPoint> connectedClients = new List <IPEndPoint>();

                //Initialise the DFS before creating the test object to ensure the correct port and IP are used as the seed
                DFS.Initialise(10000);

                //Create the item to be distributed
                List <ConnectionInfo> seedConnectionInfoList = (from current in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP) select new ConnectionInfo(ConnectionType.TCP, NetworkComms.NetworkIdentifier, current, true)).ToList();

                DistributedItem newItem = new DistributedItem("exampleItem", "example1", new MemoryStream(someRandomData), seedConnectionInfoList, DataBuildMode.Memory_Single);

                NetworkComms.ConnectionEstablishShutdownDelegate clientEstablishDelegate = (connection) =>
                {
                    lock (listLocker)
                        connectedClients.Remove((IPEndPoint)connection.ConnectionInfo.RemoteEndPoint);

                    Console.WriteLine("Client " + connection.ConnectionInfo + " connected.");
                };

                NetworkComms.ConnectionEstablishShutdownDelegate clientShutdownDelegate = (connection) =>
                {
                    lock (listLocker)
                        connectedClients.Remove((IPEndPoint)connection.ConnectionInfo.RemoteEndPoint);

                    Console.WriteLine("Client " + connection.ConnectionInfo + " disconnected.");
                };

                NetworkComms.PacketHandlerCallBackDelegate <int> ReplyDelegate = (packetHeader, connection, incomingObject) =>
                {
                    //Push the item into the swarm
                    lock (listLocker)
                        if (!connectedClients.Contains(connection.ConnectionInfo.RemoteEndPoint))
                        {
                            connectedClients.Add((IPEndPoint)connection.ConnectionInfo.RemoteEndPoint);
                        }

                    DFS.PushItemToPeer(connection, newItem, "BigDataRequestResponse");
                    Console.WriteLine("Pushing item to " + connection.ConnectionInfo + " (" + connection.ConnectionInfo.NetworkIdentifier + "). {0} in swarm. P#={1}, S#={2}.", connectedClients.Count, newItem.PushCount, newItem.TotalChunkSupplyCount);
                };

                NetworkComms.PacketHandlerCallBackDelegate <string> InfoDelegate = (packetHeader, connectionId, incomingString) =>
                {
                    Console.WriteLine(" ... " + connectionId + " - " + incomingString);
                };

                Console.WriteLine(" ... DFS has been initialised.");

                NetworkComms.AppendGlobalConnectionEstablishHandler(clientEstablishDelegate);
                NetworkComms.AppendGlobalConnectionCloseHandler(clientShutdownDelegate);
                NetworkComms.AppendGlobalIncomingPacketHandler("BigDataRequest", ReplyDelegate);
                NetworkComms.AppendGlobalIncomingPacketHandler("ClientInfo", InfoDelegate);

                Console.WriteLine("\nListening for incoming connections on:");
                foreach (IPEndPoint localEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP))
                {
                    Console.WriteLine("{0}:{1}", localEndPoint.Address, localEndPoint.Port);
                }

                Console.WriteLine("\nIdentifier - {0}", NetworkComms.NetworkIdentifier);
                Console.WriteLine("\nPress 's' to write out stats, 'q' to close any connected peers, 'ctrl+q' to close this host.\n");

                while (true)
                {
                    ConsoleKeyInfo pressedKey = Console.ReadKey(true);
                    #region Host Shutdown
                    if (pressedKey.Modifiers != ConsoleModifiers.Control && pressedKey.Key == ConsoleKey.Q)
                    {
                        Console.WriteLine("Sending shutdown to clients...");
                        lock (listLocker)
                        {
                            for (int i = 0; i < connectedClients.Count; i++)
                            {
                                try
                                {
                                    TCPConnection.GetConnection(new ConnectionInfo(connectedClients[i])).SendObject("ClientCommand", 0);
                                }
                                catch (Exception)
                                {
                                    Console.WriteLine("Exception telling client to shutdown. Probably already disconnected.");
                                }
                            }
                        }
                    }
                    else if (pressedKey.Modifiers == ConsoleModifiers.Control && pressedKey.Key == ConsoleKey.Q)
                    {
                        Console.WriteLine("Sending shutdown to clients and closing local host...");
                        lock (listLocker)
                        {
                            for (int i = 0; i < connectedClients.Count; i++)
                            {
                                try
                                {
                                    TCPConnection.GetConnection(new ConnectionInfo(connectedClients[i])).SendObject("ClientCommand", 0);
                                }
                                catch (Exception)
                                {
                                    Console.WriteLine("Exception telling client to shutdown. Probably already disconnected.");
                                }
                            }
                        }

                        Console.WriteLine("Closing host.");
                        break;
                    }
                    else if (pressedKey.Key == ConsoleKey.S)
                    {
                        #region Stats
                        Console.WriteLine("\nCurrent Stats:");
                        Console.WriteLine("{0} comms connections.", NetworkComms.TotalNumConnections());

                        if (NetworkComms.TotalNumConnections() > 0)
                        {
                            Console.WriteLine("Connections with: ");
                            var connections = NetworkComms.GetExistingConnection();
                            foreach (var connection in connections)
                            {
                                Console.WriteLine("\t{0}", connection.ConnectionInfo);
                            }
                        }
                        #endregion
                    }
                    #endregion
                }
                #endregion
            }
            else if (!hostMode)
            {
                //Prepare DFS in peer mode
                #region PeerMode
                Console.WriteLine("\n ... peer mode selected.");

                try
                {
                    ConnectionInfo serverConnectionInfo = new ConnectionInfo("192.168.0.105", 10000);
                    //ExampleHelper.GetServerDetails(out serverConnectionInfo);

                    DFS.Initialise(10000);
                    Console.WriteLine(" ... DFS has been initialised.");

                    bool     shutDown      = false;
                    bool     buildComplete = true;
                    DateTime startTime     = DateTime.Now;

                    int buildCount = 0;

                    NetworkComms.PacketHandlerCallBackDelegate <byte[]> ReplyDelegate = (packetHeader, connection, dataBytes) =>
                    {
                        try
                        {
                            buildCount++;
                            DistributedItem item = DFS.MostRecentlyCompletedItem();
                            Console.WriteLine(" ... full item build " + buildCount + " took {0} secs ({1} MB/s) using {2} total peers. {3} builds completed.", (DateTime.Now - startTime).TotalSeconds.ToString("0.00"), (((double)dataBytes.Length / 1048576.0) / (DateTime.Now - startTime).TotalSeconds).ToString("0.0"), item.SwarmChunkAvailability.NumPeersInSwarm(), buildCount);

                            double speed = (((double)dataBytes.Length / 1048576.0) / (DateTime.Now - startTime).TotalSeconds);
                            connection.SendObject("ClientInfo", " ... build " + buildCount + " took " + (DateTime.Now - startTime).TotalSeconds.ToString("0.00") + " secs (" + speed.ToString("0.0") + " MB/s) using " + item.SwarmChunkAvailability.NumPeersInSwarm() + " peers. " + buildCount + " builds completed.");
                            buildComplete = true;
                            dataBytes     = null;
                            GC.Collect();
                        }
                        catch (Exception ex)
                        {
                            LogTools.LogException(ex, "DFSTestCallbackError");
                            Console.WriteLine("Shutting down due to exception.");
                            shutDown = true;
                        }
                    };

                    NetworkComms.PacketHandlerCallBackDelegate <int> ShutdownDelegate = (packetHeader, connectionId, packetDataBytes) =>
                    {
                        shutDown = true;
                    };

                    NetworkComms.AppendGlobalIncomingPacketHandler("BigDataRequestResponse", ReplyDelegate);
                    NetworkComms.AppendGlobalIncomingPacketHandler("ClientCommand", ShutdownDelegate);

                    Console.WriteLine("\nIdentifier - {0}", NetworkComms.NetworkIdentifier);
                    Console.WriteLine("\nListening for incoming objects on:");
                    foreach (IPEndPoint localEndPoint in Connection.ExistingLocalListenEndPoints(ConnectionType.TCP))
                    {
                        Console.WriteLine("{0}:{1}", localEndPoint.Address, localEndPoint.Port);
                    }

                    startTime = DateTime.Now;

                    //while (true)
                    //{
                    if (!shutDown && buildComplete)
                    {
                        //Console.WriteLine("\nPress 'r' to rebuild or any other key to shutdown.");
                        //var shutdownKey = Console.ReadKey(true).Key;
                        //if (shutdownKey != ConsoleKey.R) shutDown = true;

                        //if (!shutDown)
                        //{
                        DistributedItem item = DFS.MostRecentlyCompletedItem();
                        if (item != null)
                        {
                            DFS.RemoveItem(item.Data.CompleteDataCheckSum);
                            Console.WriteLine("\n ... item removed from local and rebuilding at {0}.", DateTime.Now.ToString("HH:mm:ss.fff"));
                            startTime = DateTime.Now;
                        }

                        buildComplete = false;

                        TCPConnection.GetConnection(serverConnectionInfo).SendObject("BigDataRequest");

                        Console.WriteLine(" ... initiating item build ...");
                        //}
                    }
                    else if (shutDown)
                    {
                        shutDown = true;
                        DFS.Shutdown();
                        //break;
                    }

                    Thread.Sleep(250);
                    //}

                    while (!buildComplete)
                    {
                        Thread.Sleep(250);
                    }

                    try
                    {
                        TCPConnection.GetConnection(serverConnectionInfo).SendObject("ClientInfo", "... shutting down, initiating DFS shutdown.");
                    }
                    catch (CommsException)
                    {
                        Console.WriteLine("... unable to inform local of shutdown. Connection probably already closed.");
                    }

                    Console.WriteLine("Done. Completed {0} builds.", buildCount);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Bad Error!");
                    LogTools.LogException(ex, "DFSTestError_" + NetworkComms.NetworkIdentifier);
                }

                #endregion
            }

            DFS.Shutdown();
            NetworkComms.Shutdown();
        }