Used to send all or parts of a stream. Particularly useful for sending files directly from disk etc.
Inheritance: IDisposable
        /// <summary>
        /// Discover peers using TCP port scan
        /// </summary>
        /// <param name="discoverTimeMS"></param>
        /// <returns></returns>
        private static Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>> DiscoverPeersTCP(int discoverTimeMS)
        {
            #region Determine All Possible Peers/Port Combinations
            //Get a list of all IPEndPoint that we should try and connect to
            //This requires the network and peer portion of current IP addresses
            List<IPEndPoint> allIPEndPointsToConnect = new List<IPEndPoint>();

            //Look at all possible addresses
            foreach (var iFace in NetworkInterface.GetAllNetworkInterfaces())
            {
                bool interfaceValid = false;
                var unicastAddresses = iFace.GetIPProperties().UnicastAddresses;

                //Check if this adaptor is allowed
                if (HostInfo.RestrictLocalAdaptorNames != null)
                {
                    foreach (var currentName in HostInfo.RestrictLocalAdaptorNames)
                    {
                        if (iFace.Name == currentName)
                        {
                            interfaceValid = true;
                            break;
                        }
                    }
                }
                else
                    interfaceValid = true;

                //If the interface is not allowed move to the next adaptor
                if (!interfaceValid)
                    continue;

                //If the adaptor is allowed we can now investigate the individual addresses
                foreach (var address in unicastAddresses)
                {
                    //We are only interested in IPV4 ranges. A TCPPortScan on an IPV6 range may take a while
                    if (address.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork &&
                        !IPRange.IsAutoAssignedAddress(address.Address))
                    {
                        //Check if we have restricted the addresses
                        bool addressAllowed = true;
                        if (HostInfo.IP.RestrictLocalAddressRanges != null)
                            addressAllowed = IPRange.Contains(HostInfo.IP.RestrictLocalAddressRanges, address.Address);

                        if (addressAllowed)
                        {
                            //Generate all possible IPEndPoints for the current address and subnetmask
                            //We have a special catch for the loopback address which has a very large range
                            List<IPAddress> addressesInRange;
                            if (address.Address.Equals(IPAddress.Loopback))
                                addressesInRange = new List<IPAddress>() { IPAddress.Loopback };
                            else
                            {
                                IPRange range = new IPRange(address.Address, address.IPv4Mask);
                                addressesInRange = range.AllAddressesInRange();
                            }

                            foreach (IPAddress currentAddressInRange in addressesInRange)
                            {
                                for (int port = MinTargetLocalIPPort; port <= MaxTargetLocalIPPort; port++)
                                    allIPEndPointsToConnect.Add(new IPEndPoint(currentAddressInRange, port));
                            }
                        }
                    }
                }
            }
            #endregion

            #region Send Discovery Packet & Wait
            //For each address send the discovery packet
            SendReceiveOptions nullOptions = new SendReceiveOptions<NullSerializer>();
            StreamTools.StreamSendWrapper sendStream =
                new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(new MemoryStream(new byte[0])));

            int previousConnectionTimeout = NetworkComms.ConnectionEstablishTimeoutMS;
            NetworkComms.ConnectionEstablishTimeoutMS = 1000;

            AutoResetEvent allSendsCompleteEvent = new AutoResetEvent(false);
            long interlockedCompletedCount = 0;
            object _syncRoot = new object();
            List<Connection> allConnections = new List<Connection>();

            //Get unconnected TCP connections
            foreach (IPEndPoint remoteEndPoint in allIPEndPointsToConnect)
            {
                Connection conn = TCPConnection.GetConnection(new ConnectionInfo(remoteEndPoint), false);
                conn.AppendIncomingPacketHandler<byte[]>(discoveryPacketType, PeerDiscoveryHandler);
                allConnections.Add(conn);
            }

            using (Packet sendPacket = new Packet(discoveryPacketType, sendStream, nullOptions))
            {
                foreach (Connection conn in allConnections)
                {
                    Connection innerConnection = conn;

                    //The longest wait for the port scan is the TCP connect timeout
                    //The thread pool will start a large number of threads (each of which does very little)
                    // to greatly speed this up
                    _tcpPortScanThreadPool.EnqueueItem(QueueItemPriority.Normal, (state) =>
                    {
                        try
                        {
                            try
                            {
								innerConnection.EstablishConnection();
								innerConnection.SendPacket<byte[]>(sendPacket);
                            }
                            catch (CommsException)
                            {
                            }

                            lock (_syncRoot)
                            {
                                interlockedCompletedCount++;
                                if (interlockedCompletedCount == allIPEndPointsToConnect.Count)
                                    allSendsCompleteEvent.Set();
                            }
                        }
                        catch (Exception) { }
                    }, null);
                }

                allSendsCompleteEvent.WaitOne();
            }

            NetworkComms.ConnectionEstablishTimeoutMS = previousConnectionTimeout;
            
            sendStream.ThreadSafeStream.Dispose(true);

            AutoResetEvent sleep = new AutoResetEvent(false);
            //We wait at least 1 second so that connected peers can respond
            //If we do not wait and close all connections immediately we may miss some replies
#if NET2
            sleep.WaitOne(Math.Max(discoverTimeMS, 500), false);
#else
            sleep.WaitOne(Math.Max(discoverTimeMS, 500));
#endif

            //Close any connections we may have established
            foreach (Connection conn in allConnections)
            {
                try
                {
                    conn.CloseConnection(false);
                }
                catch (CommsException) { }
            }
            #endregion

            #region Return Discovered Peers
            Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>> result = new Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>>();
            lock (_syncRoot)
            {
                foreach (var idPair in _discoveredPeers)
                {
                    if (!result.ContainsKey(idPair.Key))
                        result.Add(idPair.Key, new Dictionary<ConnectionType, List<EndPoint>>());

                    foreach (var typePair in idPair.Value)
                    {
                        if (!result[idPair.Key].ContainsKey(typePair.Key))
                            result[idPair.Key].Add(typePair.Key, new List<EndPoint>());

                        foreach (var endPoint in typePair.Value)
                            if (!result[idPair.Key][typePair.Key].Contains(endPoint.Key))
                                result[idPair.Key][typePair.Key].Add(endPoint.Key);
                    }
                }
            }

            return result;
            #endregion
        }
        /// <summary>
        /// Discover peers using UDP broadcast
        /// </summary>
        /// <param name="discoverTimeMS"></param>
        /// <returns></returns>
        private static Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>> DiscoverPeersUDP(int discoverTimeMS)
        {
            SendReceiveOptions nullOptions = new SendReceiveOptions<NullSerializer>();
            StreamTools.StreamSendWrapper sendStream =
                new StreamTools.StreamSendWrapper(new StreamTools.ThreadSafeStream(new MemoryStream(new byte[0])));

            using (Packet sendPacket = new Packet(discoveryPacketType, sendStream, nullOptions))
            {
                for (int port = MinTargetLocalIPPort; port <= MaxTargetLocalIPPort; port++)
                    UDPConnection.SendObject<byte[]>(sendPacket, new IPEndPoint(IPAddress.Broadcast, port), nullOptions, ApplicationLayerProtocolStatus.Enabled);
            }

            sendStream.ThreadSafeStream.Dispose(true);

            AutoResetEvent sleep = new AutoResetEvent(false);
#if NET2
            sleep.WaitOne(discoverTimeMS, false);
#else
            sleep.WaitOne(discoverTimeMS);
#endif

            Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>> result = new Dictionary<ShortGuid, Dictionary<ConnectionType, List<EndPoint>>>();
            lock (_syncRoot)
            {
                foreach (var idPair in _discoveredPeers)
                {
                    if(!result.ContainsKey(idPair.Key))
                        result.Add(idPair.Key, new Dictionary<ConnectionType,List<EndPoint>>());

                    foreach(var typePair in idPair.Value)
                    {
                        if(!result[idPair.Key].ContainsKey(typePair.Key))
                            result[idPair.Key].Add(typePair.Key, new List<EndPoint>());

                        foreach(var endPoint in typePair.Value)
                            if(!result[idPair.Key][typePair.Key].Contains(endPoint.Key))
                                result[idPair.Key][typePair.Key].Add(endPoint.Key);
                    }
                }
            }

            return result;
        }