Download Latest Version BeanNetworkTester-v0.4.0-windows-x64.zip (11.4 MB)
Email in envelope

Get an email when there's a new version of Bean Network Tester

Home / v0.4.0
Name Modified Size InfoDownloads / Week
Parent folder
BeanNetworkTester-v0.4.0-windows-x64.zip 2026-08-01 11.4 MB
SHA256SUMS.txt 2026-08-01 107 Bytes
0.4.0 source code.tar.gz 2026-08-01 1.3 MB
0.4.0 source code.zip 2026-08-01 1.4 MB
README.md 2026-08-01 24.5 kB
Totals: 5 Items   14.1 MB 25

If it saved you time, leave a star. That is how the next tester who needs it finds out it exists. BeanStar

The short version. This release is about numbers you can trust and targeting that catches what you aimed at. If you read nothing else:

  • Aiming at a process now catches a connection from its first packet, TCP and UDP alike. "Drop SYN" combined with a process target previously did nothing at all, and DNS and QUIC walked past untouched.
  • "Effective loss" was wrong and is now right. It counts every impairment, over the traffic you aimed at. A session losing 90% of its traffic to a speed limit used to report 0.0%.
  • Presets and the shipped scenarios were recalculated against published measurements. Latency was applied twice, and three presets were eight times too fast.
  • A session moves about 1.3x more packets a second, and a delay you ask for arrives without the old few-millisecond surcharge.
  • New: "Capture only the targeted traffic". On a real run without it, the driver threw away 43% of the traffic you had aimed at before this tool ever saw it.
  • New: "Show only the targeted traffic", five new presets, a "Driver queue wait" reading, and a warning when your target shares a port with another program.
  • Config and scenario files now reject a typo instead of ignoring it.
  • Plus a long list of fixes to the counters, the Connections table and session start/stop.

If you script this tool, read the BREAKING section: presets, "Effective loss", profiles, the statistics CSV, the connections CSV and the reproduction report all changed shape or meaning.

BREAKING

  • A misspelled setting in a config file is now an error. "latancy": 300 used to load in silence, so --dry-run said "Configuration is valid" and the run went out with no latency at all. Unknown settings now fail with code 3, naming the key and suggesting the near miss. Files this tool saved keep loading. A hand-written file with an unknown name will not.

  • A mistake in a scenario file now says so instead of doing nothing. "duraton" used to leave a reset at its default and "lop" used to turn looping off, both silently, so the tool looked like it was ignoring your file. Unknown keys, in a step or at the top level, are now an error naming the key. A correct file keeps working.

  • A scenario that ends now ends the run. A non-looping scenario with a timeline used to print "Scenario finished." and then run forever, which in a pipeline is a job that hangs to its own timeout and writes no summary. Such a run now stops with stop_reason: "scenario_done" and says so at the start. --duration still wins wherever you pass it.

  • The old reset_now scenario action is gone. reset_tcp does the same thing and is now the only action. A file still using reset_now will not load and says which step to fix. The "Reset TCP now" button is unaffected.

  • The presets were wrong and are now checked against published measurements. Latency was set as if it were a ping, but it is added in both directions, so "Satellite link" at 600 ms delivered a 1200 ms ping. Three presets held kilobits where the field wants kilobytes, making them eight times too fast. If you scripted --preset, your traffic changes. Ids are unchanged, but --preset "Satellite link" is now "Satellite (geostationary)".

  • The shipped scenarios in scenarios/ were recalculated the same way, so a scenario and a preset describe the same network with the same numbers. The ping you get is now the number written in the file.

  • "Effective loss" now means what its name says, and your numbers will change. It used to be the configured Loss percentage over every packet seen, ignoring speed limits, blocking, LAN cut, link outages, resets, dropped SYNs and expired NAT. It now counts every impairment, over the traffic you aimed at. effective_loss_pct and effective_corruption_pct moved with it, so reports from before and after are not comparable.

  • "Duplicated" counts packets actually sent twice, not packets the tool decided to duplicate. Under load the copy was thrown away while the counter rose anyway: 40 packets in, "Duplicated" read 40, nothing reached the wire. This matches "Corrupted", which always counted only real changes. Reports from before and after are not comparable on that field.

  • Profiles now remember more of the link: latency spikes, link outages (flapping) and the buffer, on top of the original seven fields. Outages are why it was worth doing - a profile can finally describe a link that cuts out. Old profiles load exactly as before. Picking a preset now clears all of these, so "Perfect network" really does clear everything, and --preset finally does what the window does.

  • The Connections table now shows what arrived AND what was offered. "down", "up" and "total" held captured bytes under headings meaning delivered, so a row could read 5 MB while the application got 0.4 MB. Those three are now delivered, and new "down seen" / "up seen" hold what was captured. The connections CSV changed shape: the three old byte columns are replaced by six delivered_* and captured_* ones, renamed rather than reused.

  • The reproduction report's "connections reset" counted packets, not connections. One reset connection could report itself as 50. The report now carries three keys that answer three questions: connections_reset, rst_packets_dropped and rst_sent. The statistics CSV gained a matching connections_reset column. Nothing on screen changed.

  • The statistics CSV gained capture_narrowed and packets_in_scope columns. The first records whether "Capture only the targeted traffic" was in effect, without which two rows under one header can count completely different traffic. Your existing file is moved aside with a timestamp and a fresh one started, as it already is whenever columns change, so no row misaligns. A script reading by column position needs the new offsets.

  • --gui no longer accepts any other option. --gui --loss 30 --duration 600 used to open no window and quietly impair in the background, with no STOP button anywhere. It now stops with a usage error (code 2) and says what to do instead. If a script relied on this, delete --gui from it.

Added

  • Five new presets: Satellite (low orbit), Distant server (another continent), Congested home link (bufferbloat), Train / metro (tunnels) and In-flight Wi-Fi. Two are worth a note: bufferbloat only bites once you really saturate the link, and Train / metro is the only preset that takes the connection fully down, so your application has to reconnect rather than just slow down.

  • "Show only the targeted traffic" (Settings, off by default) points the counters, the chart, the Connections table and the connections CSV at your target alone. It changes what you see, never what is captured or impaired. Three things deliberately do not follow it: "Queue overflow", "Dropped at stop" and "Send failed" always cover everything, because they count the tool's own losses. The statistics CSV carries both totals instead. Reports and --format json are unchanged.

  • "Capture only the targeted traffic" (--narrow-filter) pushes your destination IP and port into WinDivert, so traffic that could never be impaired is not handed over at all. It is a correctness fix, not just a speed one: measured on a real run, without it the driver threw away 43% of the traffic you aimed at before the tool saw it. It applies at START, and does nothing for a process target, a wildcard or an re: pattern - the run says which.

  • "Driver queue wait (peak)" in the Session tab shows how long packets waited inside WinDivert before the tool saw them. That is the one delay the tool adds and counted nowhere. It is measured, not estimated. Expect a fraction of a millisecond when idle; above 50 ms the log says so. Blank under --simulate.

  • A session now records the WinDivert queue it ran behind (length, time and size) in the log and in the reproduction report, so a report from a machine you do not have in front of you says which queue produced its numbers.

  • The tool now warns when your target shares a port with another program. Windows lets several programs hold one local port - that is how mDNS, SSDP and DHCP work - and this tool decides what to break from the port number. On this machine four ports out of 127 were shared, one of them by five programs. Applying a target now names the port and who holds it, so you know that part of the result is a coin toss.

  • The command line says when your target stops matching, instead of finishing green in silence. Measured: aiming by process id and restarting the program left five out of five new connections untouched with nothing in the output. Aiming by name recovers on its own and costs only the first connection. Aiming by process id never recovers. If the program under test restarts, aim by name.

  • Every run with a process target ends by saying how much of the captured traffic was yours - "In scope: 40 of 500 captured packets" - and calls it out when that is zero. A run where your target caught nothing looks exactly like a run where your application coped. It is a warning, not a failure, because a quiet target is perfectly ordinary.

Changed

  • The checkbox is now called "Capture only the targeted traffic", not "Narrow the driver filter to the target". The old name described the machinery. The --narrow-filter flag is unchanged, and its tooltip was rewritten to say what it does and when it will not apply.

  • A session moves about a third more packets a second. The tool handles packets on two threads, and Python left one waiting up to 5 ms for its turn - that waiting, not the work, was the limit. A session now asks for shorter turns and hands the setting back at STOP. Measured on loopback and a real card: 1.33x to 1.36x, in 24 comparisons out of 24, at slightly less processor time per packet.

  • A packet you did not ask to change goes back on the wire exactly as it arrived, and the session moves about 12% more traffic for it. Checksums used to be recomputed for every packet, including untouched ones, which also meant a session with nothing configured did not quite pass traffic through unchanged. Measured: 1.12x more packets a second, 8 comparisons out of 8, and 24 MiB of TCP arrived byte for byte over a real card.

  • Targeting a process no longer competes with the traffic it is measuring. Working out which sockets belong to your target used to happen on the thread handling your packets, dozens of times a second. It now runs on its own thread. A freshly opened connection is still impaired within tens of milliseconds and STOP stays immediate.

  • Semicolons are gone from the interface texts and both READMEs. Twenty-one tooltips and about eighty lines of documentation used them to join sentences, which is not how people write. Code samples keep theirs, where a semicolon is syntax.

  • "Spike chance" and "Spike size" moved to "Latency (ping)" from "Advanced (NAT / connections)", because a spike is latency. Nothing about how they work changed, nor their flags, nor their place in a profile.

  • "Blocking (firewall)" starts collapsed on a fresh install, like the other advanced panels. If you have used the tool before, your own choices are remembered.

  • The Latency and Jitter tooltips say the thing that was easy to get wrong: ping rises by about twice the latency you set, because both the request and the reply are delayed, while jitter widens the wobble by about 1.4x rather than doubling it. Both READMEs explain it too.

Fixed

  • Aiming at a process missed the first packet of every new connection. Measured: 20 fresh connections with "Drop SYN" at 100% produced 20 successful connections and not one dropped SYN, so "Drop SYN" plus a process target did nothing at all. The first packet is now checked against the live socket map. What changes for you: connections aimed at a process now take longer to open and a minority fail outright, which is what a bad network does. If a test measured "time to first byte" under impairment, expect it to move.

  • Aiming at a process did not touch the first packet of a UDP exchange - and DNS and QUIC take a fresh port every time, so it did not touch them at all. If you tested a game, a video call or a browser over QUIC and the impairment seemed weaker than configured, this is why. Ordinary TCP data is deliberately still not re-checked: measuring showed it would cost throughput for nothing.

  • The live "which app owns this port" map could be dragged backwards by a stale reading, so a connection was briefly credited to the wrong application. A periodic sweep of the socket table, always a little behind, was applied on top of the live signal from the driver. Measured over 25 seconds of ordinary traffic: 919 times. Readings are now weighed by when they were taken.

  • Targeting could follow a process id after the process was gone, and Windows hands those numbers out again. A restarted target could come back under a remembered number and not be impaired, while an unrelated program inheriting that number was. The tool now checks the process is still the same one, and forgets it when it closes its last connection.

  • Three fixes to short-lived and freshly opened connections. Targeting now follows the system's socket events instead of scanning a few times a second, so a connection that opens and closes between two scans is no longer missed. The Connections table fills in the owning program from the first packet instead of leaving the row blank. And setting a process target no longer makes the first START pause for a second or two.

  • The "impaired?" column now reflects the whole session, not just this instant. It asked whether the port was in the target right now, so a row flipped to "no" the moment its connection closed and a run impairing all of Chrome looked like it caught almost nothing. The column, the row highlight, the sort and the CSV now read one record, so they cannot disagree.

  • The tool kept pausing itself and then blaming WinDivert. Every so often it spent up to half a second working out which program owns which connection, stopped collecting packets while it did, and then told you the driver's queue was backing up and to narrow your filter. The delay was its own. Measured over 95 seconds with programs constantly starting: the worst pile-up dropped from 508 ms to 17 ms and the warning stopped appearing.

  • The "driver held a packet" warning described lost accuracy when what you were losing was traffic. Measured on a deliberately overloaded run: at 138,000 packets a second offered, the tool moved about 14,000 and 91.75% was thrown away by the driver before the tool saw it, while every drop counter on screen read zero. The warning now says a full driver queue means dropped packets, not just late ones.

  • With "Capture only the targeted traffic" on, the Statistics and Connections tabs said the exact opposite of the truth, keeping their "counters cover ALL captured traffic" line while the driver had been told to hand over nothing else. The checkbox tooltip promised the opposite and both READMEs contradicted themselves. Both notes, the chart caption and the READMEs now describe what the figures actually cover, including the case where a process target is set as well.

  • Four fixes to the Scope settings. The two "only the targeted traffic" switches now sit in one Scope card instead of separate panels a few rows apart, with a line that tells you before START whether your destination can actually be narrowed. The Settings window scrolls, so no group falls off the bottom. Starting a session logs which of the two outcomes you got. And the Session panel gained a "Capture" row, so a saved run says which traffic it counted.

  • Three counters were lying about how much was lost. "Buffer overflow" and "Dropped at stop" charged for duplicate copies as well as packets, so a run duplicating everything reported nearly twice as many dropped as captured. A connection's "dropped" count ignored packets its own queue threw away. And packets the tool failed to re-inject left the arithmetic entirely - those now have a "Send failed" counter, a banner and an event-log entry, throttled so they cannot flood the log.

  • Three tooltips were telling you things that were not true. "Dropped" claimed to count link outages, which have had their own counter for a while. "Downloaded (MB)" promised a figure that never appeared. And "Effective loss" read as "never reached the far end" when it measures damage done on this machine - loss out in the network never arrives here, so nothing here can count it. No numbers changed.

  • The delay you set is the delay you get. Windows rounds up the wait used to hold a packet back, and it was a fixed surcharge rather than a percentage, so it barely showed at 100 ms and swamped small settings. Measured with a plain ping: asking for 10 ms used to cost 12.6 ms extra and now costs 1 ms, the same 1 ms as at 50 ms. Jitter below about 15 ms used to vanish into the noise.

  • Long cut-offs and NAT blackouts now last as long as you set them. The tool forgets a connection it has not seen for a while, and a forgotten connection looks brand new - so a reset cooldown above about half a minute resumed after roughly 30 seconds however long you asked for, and "NAT mapping expiry" at 30 seconds never blocked a single packet.

  • "NAT mapping expiry" now really cuts the incoming direction. The packet rejected for "the mapping has expired" was itself counted as activity, so the mapping reopened on the spot: an app that never sent a keep-alive lost about one packet every five seconds and otherwise worked, passing a test it should have failed. Incoming traffic now stays cut until the application sends something.

  • "Reset connections" now works where it claimed to. It no longer fires on a connection that is still opening, where the forged reset carries no acknowledgement number and Windows is entitled to ignore it - measured, the connection hung until its own timeout. And it now really resets local (loopback) connections, which previously just went quiet for the cooldown while the tool reported an RST as sent. Connections to other machines were never affected.

  • Ping traffic never appeared in the Connections tab, which claims to list all of them - anything without ports was silently left out. Thirty seconds of pinging left the tab empty while the counters ticked up beside it. Portless traffic is now listed as one row per address with the port cells empty.

  • The Connections tab could empty itself instead of dropping its oldest rows. When many rows carried the same timestamp they all fell on the same side of the "old" estimate, so far more went than intended - in the extreme, everything. It now refuses to drop below the level it is trimming to.

  • When the capture could not start, the tool told you the wrong reason. A handle that failed to open produced WinDivert handle is not open - a symptom naming nothing - while the real cause went to a diagnostic file nobody reads. The helpful messages both interfaces already had, including the window's "run as Administrator" hint, could therefore never appear. Starting now fails immediately, with the reason.

  • Four ways a session mishandled its own start and stop. A failed start could leave the tool holding your traffic without impairing it, refusing every later START. Ending a session kept hold of your traffic for a moment while tidying up, so a connection could stall. STOP could take two seconds in two different races. And the log could print a fault above its own "Start" line.

  • An unforeseen error during a run is now an exit code, not a stack trace. A failure inside the reporting loop escaped as a raw traceback with code 1, which is also "the session could not start", so a pipeline could not tell an internal bug from a driver that would not open. It is now runtime (1) with a line saying what happened, and the run still writes its complete summary - a --format json file no longer ends mid-stream.

  • Three fixes to --dry-run and --doctor claiming more than they checked. --dry-run said "Configuration is valid" about a command that then exits 7, so it now says which half it checked and points at --doctor. It also reads your --scenario file now, which it never opened. --doctor no longer calls a driver "not loaded" when Windows simply refused to let it look.

  • Three fixes to the shared-port warning. It listed your own target among the strangers, because a program like Chrome runs several processes. It offered two possible outcomes when the socket table already decides which one applies. And it printed bare numbers, so 5353 read like a fault rather than mDNS. It now says 5353 (mDNS), names the program, and states the one outcome that is true.

  • Three files could stop the program from starting, and no longer do. A translation file with a malformed header, a window-layout file with an entry in the wrong shape, and a --config file that was valid JSON but not a set of settings - the last one reported the tool as having crashed. Each is now skipped or reported clearly, and the rest of your setup is kept.

  • Four smaller interface fixes. The throughput chart could be squeezed until it vanished on a narrow window (the Live tab now scrolls). "Close" was cut in half at the bottom of the Settings window. The profile picker cut long names off at 24 characters. And "Save profile..." opens with the cursor already in the name box.

  • Two things left litter behind. A failed "Export connections CSV" left a stray .tmp file next to the real one, and stopping a session could file a crash report in crashes/ for an ordinary STOP. Neither happens now.

  • Starting a second scenario without stopping the first is no longer possible. The engine replaced the running one and left the old one going in the background, so two scenarios fought over the same settings. Nothing in the program does this today, so it is a guard rather than a bug you could have hit.

Docs

  • Both READMEs now document the scenario file format, the seven shipped scenarios, both CSV exports and all 17 Connections columns - none of which were written down anywhere. The column meanings existed only as tooltips, and the CSV headers only in the code.

  • Three corrections to what the READMEs claimed. A connection is in scope "the moment it opens" only for a program the tool already recognises, not for that program's first connection. An exclusion like !chrome also covers every connection whose owner could not be identified, so do not use one to protect an application - name the one you do want broken. And both files explained greyed-out fields with an "Enable" checkbox that no longer exists.

  • The Requirements and Tests sections now match reality. psutil is not what makes process targeting work on Windows - the socket table and process names come from the OS, and targeting keeps working without it. Source installs work on Python 3.10 and newer while CI tests and builds on 3.14 only, which is the version frozen into the released .exe. The Tests section also now covers the GUI render check and how a release is produced.

Source: README.md, updated 2026-08-01