/// <summary> /// Pulls updates from the FlooderThreads and builds combined FlooderResult objects for LatestUpdate and HistoryResult. /// </summary> void ProcessUpdates() { // Sum latest results from all threads. FlooderResult resultSum = new FlooderResult(); for (int i = 0; i < Action.Concurrency; i++) { resultSum += ft[i].LatestResult; UpdateReceived[i] = false; } LatestResult = resultSum; // Add to History[] array, and recompute single HistoryResult. History[HistoryCounter] = resultSum; HistoryCounter = (HistoryCounter + 1) % History.Length; FlooderResult historyResult = new FlooderResult(); for (int i = 0; i < History.Length; i++) { if (History[i] != null) { historyResult += History[i]; } } HistoryResult = historyResult; }
/// <summary> /// Stops all threads, and clears all Results objects. /// </summary> public void Stop() { foreach (FlooderThread t in ft) { t.Stop(); } UIUpdateTimer.Stop(); IsRunning = false; LatestResult = new FlooderResult(); HistoryResult = new FlooderResult(); for (int i = 0; i < History.Length; i++) { History[i] = null; } }
public Flooder() { Action = new FlooderAction(); IsRunning = false; ft = new FlooderThread[Action.Concurrency]; UpdateReceived = new bool[Action.Concurrency]; LatestResult = new FlooderResult(); History = new FlooderResult[5000 / Action.TimeQuantum]; HistoryCounter = 0; UIUpdateTimer = new System.Timers.Timer(Action.TimeQuantum); UIUpdateTimer.Elapsed += UIUpdateTimer_Elapsed; for (int i = 0; i < Action.Concurrency; i++) { ft[i] = new FlooderThread(Action); ft[i].PropertyChanged += FlooderThread_PropertyChanged; } }
/// <summary> /// Adds two FlooderResult objects together. /// </summary> /// <param name="fr1">FlooderResult object #1</param> /// <param name="fr2">FlooderResult object #2</param> /// <returns>Combined FlooderResult object</returns> public static FlooderResult operator +(FlooderResult fr1, FlooderResult fr2) { var fr = new FlooderResult(); fr.StartTime = fr1.StartTime < fr2.StartTime ? fr1.StartTime : fr2.StartTime; fr.EndTime = fr1.EndTime > fr2.EndTime ? fr1.EndTime : fr2.EndTime; fr.OctetsSent = fr1.OctetsSent + fr2.OctetsSent; fr.PacketsSent = fr1.PacketsSent + fr2.PacketsSent; fr.OctetsReceived = fr1.OctetsReceived + fr2.OctetsReceived; fr.PacketsReceived = fr1.PacketsReceived + fr2.PacketsReceived; fr.SumOfLatency = fr1.SumOfLatency + fr2.SumOfLatency; if (fr1.PacketsReceived > 0) { fr.PacketsLost = fr1.PacketsLost; } if (fr2.PacketsReceived > 0) { fr.PacketsLost = (fr.PacketsLost.HasValue) ? fr.PacketsLost + fr2.PacketsLost : fr2.PacketsLost; } fr.SeqLastSeen = (fr1.SeqLastSeen > fr2.SeqLastSeen) ? fr1.SeqLastSeen : fr2.SeqLastSeen; return(fr); }
/// <summary> /// Receives UDP traffic, parses the payload, and records the arrival details. /// </summary> /// <param name="StopTime">Run until Stopwatch SW registers this number of ticks elapsed.</param> /// <param name="MinSeqNumber">The minimum sequence number to expect. Computed by sender thread.</param> /// <param name="MaxSeqNumber">The maximum sequence number to expect. Computed by sender thread.</param> /// <returns>Results from receive operations</returns> private FlooderResult Receive(long StopTime, ulong MinSeqNumber, ulong MaxSeqNumber) { int recvBytes = 0; long timeReceived = 0; byte[] seqReceivedBytes = new byte[8]; byte[] timeReceivedBytes = new byte[8]; FlooderResult result = new FlooderResult(); if (MaxSeqNumber - MinSeqNumber > Math.Pow(2, 24)) { MinSeqNumber = MaxSeqNumber - (ulong)Math.Pow(2, 24); } int SequenceBuffer = (int)(MaxSeqNumber - MinSeqNumber); bool[] SequenceReceived = new bool[SequenceBuffer]; while (SW.ElapsedTicks < StopTime) { while (UDPSocket.Available > 0) { try { recvBytes = UDPSocket.Receive(recvBuffer, recvBuffer.Length, SocketFlags.None); } catch (SocketException) { } finally { timeReceived = SW.ElapsedTicks; result.PacketsReceived++; result.OctetsReceived += recvBytes + HeaderLen; Array.ConstrainedCopy(recvBuffer, 0, seqReceivedBytes, 0, 8); if (BitConverter.IsLittleEndian) { Array.Reverse(seqReceivedBytes); } ulong Sequence = BitConverter.ToUInt64(seqReceivedBytes, 0); if (MinSeqNumber <= Sequence && Sequence <= MaxSeqNumber) { result.SeqLastSeen = Sequence; SequenceReceived[Sequence - MinSeqNumber] = true; } Array.ConstrainedCopy(recvBuffer, 8, timeReceivedBytes, 0, 8); if (BitConverter.IsLittleEndian) { Array.Reverse(timeReceivedBytes); } long originTime = BitConverter.ToInt64(timeReceivedBytes, 0); result.SumOfLatency += timeReceived - originTime; } } Thread.Yield(); } if (result.SeqLastSeen != null) { result.PacketsLost = 0; for (int i = 0; i < (int)((ulong)result.SeqLastSeen - MinSeqNumber); i++) { if (SequenceReceived[i] == false) { result.PacketsLost++; } } } return(result); }
/// <summary> /// Sends and receives UDP traffic in accordance with the parameters in FlooderAction. /// </summary> /// <param name="worker">BackgroundWorker object that describes the current thread. /// Used to get cancellation state and send back FlooderResult objects.</param> private void Sender(BackgroundWorker worker) { byte[] sendBuffer = null; ulong SeqNum = 0; Nullable <ulong> SeqLastSeen = null; int PacketsToSend = 0; long waitInterval = 0; Random r = new Random(); long LargestSlice = 0; FlooderResult result; TaskFactory <FlooderResult> receiverFactory = new TaskFactory <FlooderResult>(); IsRunning = true; if (UDPSocket == null) { Debug.WriteLine("Begin Init Socket"); UDPSocket = new Socket(SocketType.Dgram, ProtocolType.Udp); UDPSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 12500000); UDPSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 12500000); // Code to ignore ICMP unreachable messages and prevent the socket from closing on Receive(). // I don't care if the remote system doesn't reflect messages back. //http://stackoverflow.com/questions/7201862/an-existing-connection-was-forcibly-closed-by-the-remote-host uint IOC_IN = 0x80000000; uint IOC_VENDOR = 0x18000000; uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; UDPSocket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); UDPSocket.Connect(Action.TargetIP, Action.DestPort); if (Action.TargetIP.AddressFamily == AddressFamily.InterNetwork) { HeaderLen = 28; } else if (Action.TargetIP.AddressFamily == AddressFamily.InterNetworkV6) { HeaderLen = 48; } else { HeaderLen = 0; } Debug.WriteLine("End Init Socket"); } SW.Start(); //********** BEGIN Main Loop while (!worker.CancellationPending) { //***** Initialize state for this TimeQuantum. int packetSize = Action.PacketSize; int bandwidth = Action.BandwidthOctets; long SendStart; byte[] SeqBytes; byte[] TimeBytes; byte[] dateTimeBytes = new byte[8]; long Start; long End; // Initialize send buffer. This will vary as the send loop runs. sendBuffer = new byte[packetSize - HeaderLen]; for (int i = 0; i < sendBuffer.Length; i++) { sendBuffer[i] = 0x69; } // Figure out packet sizes, number of packets, buffer sizes, and inter-packet delay. long LifetimeInTicks = FlooderAction.TicksPerSecond * Action.TimeQuantum / 1000; double PacketsToSendDbl = ((double)bandwidth / (double)packetSize) * ((double)Action.TimeQuantum / (double)1000); PacketsToSendDbl /= Action.Concurrency; PacketsToSend = (int)PacketsToSendDbl; // It is unlikely that PacketsToSendDbl will be an integer, but I cannot send half a packet to the host without // compromising the packet size for at least one packet. This code adds an extra packet based on random chance. if (r.NextDouble() < PacketsToSendDbl - PacketsToSend) { PacketsToSend++; } if (PacketsToSend > 0) { waitInterval = LifetimeInTicks / PacketsToSend; } result = new FlooderResult(); //Debug.WriteLine("Sending " + PacketsToSend); //Debug.WriteLine("Delay " + waitInterval); ulong MaxSeqNumber = SeqNum + (ulong)PacketsToSend; ulong MinSeqNumber = SeqLastSeen == null ? 0 : (ulong)SeqLastSeen + 1; // Kick off receive thread. Lifetime is expressed in timer ticks. // new Task((resultObj) => { return Receive(long LifetimeInTicks); }); Task <FlooderResult> Receiver = receiverFactory.StartNew(() => { return(Receive(SW.ElapsedTicks + LifetimeInTicks, MinSeqNumber, MaxSeqNumber)); }); if (PacketsToSend == 0) { Thread.Sleep(Action.TimeQuantum); } else { for (int i = 0; i < PacketsToSend; i++) { // Start measuring this send packet event. SendStart = SW.ElapsedTicks; // Take the sequence number and current number of elapsed ticks, and add them to the top of the send buffer. SeqBytes = BitConverter.GetBytes(SeqNum); TimeBytes = BitConverter.GetBytes(SW.ElapsedTicks); if (BitConverter.IsLittleEndian) { Array.Reverse(SeqBytes); Array.Reverse(TimeBytes); } Array.ConstrainedCopy(SeqBytes, 0, sendBuffer, 0, 8); Array.ConstrainedCopy(TimeBytes, 0, sendBuffer, 8, 8); // Fire. try { UDPSocket.Send(sendBuffer); } catch (SocketException) { } finally { SeqNum++; result.PacketsSent++; } // The wai-ai-ting is the harr-dest paaaart. Sender() tries to be nice to other threads. However it is // unpredictable how much time Thread.Yield() will actually yield to other running threads. The largest // value I observed on my hardware was 2700 ticks. Your mileage may vary. Wildly. This code measures // the time between Yield() calls and tries to make a best guess when to start looping without yielding // time to other threads (ensuring the next packet gets off on time). while (SW.ElapsedTicks < waitInterval + SendStart - LargestSlice) { Start = SW.ElapsedTicks; Thread.Yield(); End = SW.ElapsedTicks; if (LargestSlice < End - Start) { LargestSlice = End - Start; } } while (SW.ElapsedTicks < waitInterval + SendStart) { } // Done. Repeat until all packets to send are sent. } } // Arriving at the end of TimeQuantum. // Wrap up the sender statistics. result.OctetsSent = result.PacketsSent * packetSize; result.EndTime = DateTime.Now; // Get results from receiver thread. Merge results with send thread. Send results back to UI thread. // This concludes the receiver thread. A new one will be started in the next TimeQuantum. FlooderResult receiverResult = Receiver.Result; result += receiverResult; if (result.SeqLastSeen.HasValue) { SeqLastSeen = result.SeqLastSeen; } // Send result to the main UI thread. worker.ReportProgress(0, result); result = null; //Debug.WriteLine("largest slice: " + LargestSlice); } //********** END Main Loop // Cancel was received. Shutting down. IsRunning = false; //SW.Stop(); if (UDPSocket != null) { UDPSocket.Shutdown(SocketShutdown.Both); UDPSocket.Close(0); UDPSocket = null; } }