// tool · throughput and latency testing
iperf for Wi-Fi
Most iperf guides for Wi-Fi were written between 2011 and 2015. They use iperf2 syntax on port 5001, tell you to download jperf from Google Code, and answer one question: how many megabits. Google Code shut down in 2016 and megabits stopped being the interesting question some time before that.
This page covers what actually runs today, which of the two tools to reach for, and the test that matters more than throughput on a Wi-Fi link.
// never used iperf? start here
iperf needs two ends. One machine listens (the server), the other generates traffic toward it (the client).
It measures how fast the path between them carries data. That is all it does. It is not a speed test to the internet and it does not touch your ISP.
You do not need two machines to begin. Run both halves on one computer and prove the tool works before you involve a network.
You do not need two machines to begin. Run both halves on one computer and prove the tool works before you involve a network.
STEP 1
Open two terminals
Same machine. One will be the server, one the client.
STEP 2
Terminal A
iperf3 -s
STEP 3
Terminal B
iperf3 -c 127.0.0.1 -t 5
You will see a number in the tens of Gbit/s. That is memory bandwidth, not a network measurement, and it is meant to be meaningless.
The point is that both halves ran and talked to each other. Press Ctrl-C to stop the server when you are done.
// getting it installed
| Platform | Command | Note |
|---|---|---|
| macOS | brew install iperf3 iperf | Both tools coexist. iperf3 is the binary iperf3, iperf2 is iperf. |
| Debian / Ubuntu | sudo apt install iperf3 iperf | Same split. Check versions after install. |
| RHEL / Fedora | sudo dnf install iperf3 iperf | iperf2 may be in EPEL on older releases. |
| Windows | Use iperf2, or iperf3 under WSL | iperf3 is not officially supported on Windows. Unofficial builds exist but are frequently out of date. |
| WLAN Pi | already installed | Both are present. This is the cleanest wireless-side endpoint you can carry. |
| Android | PingTools or Magic iPerf | Verify which version the app wraps before comparing results across devices. |
| Check what you have | iperf3 -v and iperf -v | Do this first. Version 3.16 is the multi-threading cutoff. |
// the thing most guides get wrong
A Wi-Fi link can saturate its throughput number and still feel broken. Once capacity is adequate, what users notice is
tail latency and service-time variance, not the average bitrate.
Bob McMahon, who maintains iperf2 at Broadcom, states this directly as his reason for continuing to develop the tool:
throughput alone is an insufficient measurement model for network behaviour.
Practical consequence: a single iperf3 -c run tells you the link is not broken. It does not tell you the link is good. Pair every throughput run with a responsiveness run.
Practical consequence: a single iperf3 -c run tells you the link is not broken. It does not tell you the link is good. Pair every throughput run with a responsiveness run.
// which tool - they are different programs, not versions
iperf2 and iperf3 share a name and nothing else. Different codebases, different maintainers, different default ports
(5001 vs 5201), and they do not interoperate.
Both are actively developed. Neither is the successor to the other.
| If you are | Use | Why |
|---|---|---|
| Testing from Windows | iperf2 | iperf3 is not officially supported on Windows. ESnet says use iperf2. |
| Multi-gig link, need max throughput | iperf3 3.16+ | One thread per stream since 3.16. ~160 Gbps observed on a 200 Gbps path. |
| Latency under load, tail behaviour | iperf2 | --bounceback, --trip-times, latency histograms. iperf3 has none of this. |
| Voice or video simulation | iperf2 | --isochronous generates frame-paced traffic with a log-normal load distribution. |
| Scripted output into a dashboard | iperf3 | -J emits clean JSON. iperf2 has -e enhanced reports but no JSON. |
| Retransmits, CWND, RTT per interval | either | iperf3 shows Retr and Cwnd on Linux. iperf2 -e adds RTT and in-flight bytes. |
| Testing a Wi-Fi 7 MLO client | iperf3 -P | MLO spreads across links. A single stream will not exercise it. |
// what should this link actually deliver?
// the commands, in the order you should run them
baseline Wired baseline first
If the wired path is the bottleneck you are measuring the switch, not the Wi-Fi. Always run this before touching a wireless client.
Server (wired host)
iperf3 -s Client (wired host)
iperf3 -c 192.168.1.10 -t 30 -i 1 downlink Client download, no server swap
The -R flag reverses direction so the server sends. This replaces the old routine of stopping everything and restarting the server on the other machine.
Server (wired host)
iperf3 -s Client (wireless)
iperf3 -c 192.168.1.10 -t 30 -i 1 -R uplink Client upload
Default direction. Client sends, server receives. On Wi-Fi this is usually slower than downlink because the client radio and its power budget are weaker than the AP.
Client (wireless)
iperf3 -c 192.168.1.10 -t 30 -i 1 parallel Parallel streams
One TCP stream rarely fills a modern Wi-Fi link. Its congestion window collapses on a single loss and takes time to recover. Four to eight streams is the realistic ceiling test.
Client
iperf3 -c 192.168.1.10 -t 30 -i 1 -P 8 Both directions at once
iperf3 -c 192.168.1.10 -t 30 --bidir udp UDP, loss and jitter
UDP does not back off, so it exposes loss and jitter that TCP hides by retransmitting. You must set -b explicitly or iperf3 defaults to 1 Mbit/s.
Client
iperf3 -c 192.168.1.10 -u -b 500M -t 30 -i 1 Read the server side
Look at Jitter and Lost/Total Datagrams, not Bitrate latency Responsiveness under load (iperf2)
This is the test that matters and almost nobody runs it. Bounceback measures round trips per second while the link is loaded. A network can saturate throughput and still feel broken.
Server
iperf -s -e -i 1 Client
iperf -c 192.168.1.10 -e -i 1 --bounceback --bounceback-congest With histograms
iperf -c 192.168.1.10 -e -i 1 --bounceback --histograms=100u,2000 voice Voice and video simulation (iperf2)
Isochronous traffic is frame-paced rather than greedy, which is what a codec actually does. 60 fps with a log-normal load distribution models video far better than a saturating TCP stream.
Server
iperf -s -u -e --histograms=100u,2000 --realtime Client
iperf -c 192.168.1.10 -u -e -i 1 --isochronous=60:100m,10m --realtime json Machine-readable output
Pipe straight into a dashboard or a diff. Far better than the Excel copy-paste routine older guides describe.
Client
iperf3 -c 192.168.1.10 -t 30 -J > run.json Pull the summary
jq '.end.sum_received.bits_per_second' run.json // reading the output - click any part
Real iperf3 client output. Every highlighted piece is explained below when you click it.
Connecting to host 192.168.1.10, port 5201 [ 5] local 192.168.1.44 port 51422 connected to 192.168.1.10 port 5201 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 62.4 MBytes 523 Mbits/sec 0 741 KBytes [ 5] 1.00-2.00 sec 70.1 MBytes 588 Mbits/sec 3 612 KBytes [ 5] 2.00-3.00 sec 68.9 MBytes 578 Mbits/sec 0 698 KBytes [ 5] 3.00-4.00 sec 41.2 MBytes 346 Mbits/sec 14 281 KBytes [ 5] 4.00-5.00 sec 67.5 MBytes 566 Mbits/sec 0 655 KBytes - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bitrate Retr [ 5] 0.00-5.00 sec 310 MBytes 520 Mbits/sec 17 sender [ 5] 0.00-5.00 sec 309 MBytes 518 Mbits/sec receiver
Click any highlighted item above to see what it means.
// practice ladder - work down, do not skip
01 Prove the tool runs
Both halves on one machine, over loopback.
iperf3 -s / iperf3 -c 127.0.0.1 -t 5 pass when
Any result at all. Tens of Gbit/s is normal and meaningless.
what it teaches
The client-server model, and that iperf needs two ends.
02 Two machines, wired
Establish the ceiling of the path before wireless enters the picture.
iperf3 -c <server-ip> -t 30 -i 1 pass when
Within about 6 percent of the link rate. 1 GbE should give 930 to 945 Mbit/s.
what it teaches
What a healthy, stable result looks like. This is your reference for everything after.
03 Reverse the direction
Measure download without restarting anything.
iperf3 -c <server-ip> -t 30 -i 1 -R pass when
Similar to Lab 02. Wired is symmetric.
what it teaches
What -R does, and why older guides swap the server instead.
04 Now go wireless
Same server, client on Wi-Fi. Note the negotiated PHY rate first.
iperf3 -c <server-ip> -t 60 -i 1 pass when
45 to 70 percent of PHY. Use the calculator above.
what it teaches
The gap between PHY rate and real throughput, and why it exists.
05 Single stream versus eight
Run the same test twice, once with -P 1 and once with -P 8.
iperf3 -c <server-ip> -t 30 -P 8 pass when
-P 8 should be meaningfully higher on a modern Wi-Fi link.
what it teaches
Why one TCP stream understates capacity, and which number to quote for which question.
06 Break it on purpose
Walk away from the AP mid-test, or turn on a microwave near a 2.4 GHz link.
iperf3 -c <server-ip> -t 60 -i 1 pass when
Watch Bitrate fall and Retr climb in the per-second intervals.
what it teaches
That the average hides everything. This is the most valuable lab on the list.
07 UDP, loss and jitter
Push past what the link can carry and watch it drop frames.
iperf3 -c <server-ip> -u -b 500M -t 30 -i 1 pass when
Non-zero Lost/Total on the server side, and jitter above 1 ms.
what it teaches
That TCP was hiding loss from you the whole time by retransmitting.
08 Responsiveness under load
Run bounceback while a throughput test saturates the same link.
iperf -c <server-ip> -e -i 1 --bounceback --bounceback-congest pass when
RPS drops sharply once the link is loaded.
what it teaches
Why a link can measure fast and feel slow. This is the lab that changes how you test.
// when it will not run
iperf3: error - unable to connect to server: Connection refused
Nothing is listening. The server side is not running, or it crashed, or you typed the wrong IP. Start with iperf3 -s on the server and confirm it prints "Server listening on 5201".
Connection timed out, no refusal
A firewall is dropping the packets silently. Open TCP 5201 for iperf3 or TCP and UDP 5001 for iperf2. On Linux: sudo ufw allow 5201/tcp. macOS and Windows will usually prompt on first run.
iperf3: error - the server is busy running a test
iperf3 serves one test at a time by default. Wait for the other test to finish, or start additional servers on other ports with -p 5202.
Control socket has closed unexpectedly
The control connection dropped mid-test, commonly on a flaky wireless link or when the client roamed. It is a symptom worth investigating, not just a tool error.
iperf2 client cannot reach iperf3 server
They are different protocols and will never interoperate. Check both ends with iperf3 -v and iperf -v. Port 5001 means iperf2, 5201 means iperf3.
UDP result shows exactly 1.00 Mbits/sec
You forgot -b. iperf3 caps UDP at 1 Mbit/s by default. Set an explicit target such as -b 500M.
Result is suspiciously round, near 940 Mbit/s
You are measuring a 1 GbE wired segment, not the Wi-Fi. Check the calculator above and move the server behind a faster link.
Throughput is far below expectation and Retr is high
Real signal. Check retries and the negotiated MCS on the client, then look for co-channel contention. Do not tune iperf flags to make the number look better.
// command builder - answer four questions, get the exact command
Run on the server
Run on the client
// udp: the numbers only exist on the server
This is the part people miss. On a UDP test the client just reports what it sent. Jitter and loss are computed by the receiver,
so they appear on the server terminal. Run --get-server-output on the client if you only have one screen.
[ ID] Interval Transfer Bitrate Jitter Lost/Total [ 5] 0.00-1.00 sec 57.2 MBytes 480 Mbits/sec 0.081 ms 0/41390 (0%) [ 5] 1.00-2.00 sec 56.8 MBytes 477 Mbits/sec 0.094 ms 0/41102 (0%) [ 5] 2.00-3.00 sec 44.1 MBytes 370 Mbits/sec 2.418 ms 9214/41088 (22%) [ 5] 3.00-4.00 sec 55.9 MBytes 469 Mbits/sec 0.112 ms 0/40450 (0%) [ 5] 4.00-5.00 sec 56.6 MBytes 475 Mbits/sec 0.088 ms 0/40936 (0%) - - - - - - - - - - - - - - - - - - - - - - - - - [ 5] 0.00-5.00 sec 271 MBytes 454 Mbits/sec 0.109 ms 9214/204966 (4.5%)
Jitter
Variation in packet arrival spacing. Under 1 ms is fine for voice. The 2.418 ms spike at second 3 is the interesting event, not the 0.109 ms average.
Lost/Total
9214 of 41088 datagrams gone in one second, 22 percent. The 4.5 percent average across the whole run makes it look survivable. It was not.
Why TCP hid this
Run the same test over TCP and you would see the bitrate dip and Retr climb, but no loss, because TCP retransmitted. UDP shows you what the air actually did.
// flag reference - the two tools side by side
| What you want | iperf3 | iperf2 | Notes |
|---|---|---|---|
| Run as server | -s | -s | Default port 5201 vs 5001 |
| Run as client | -c <ip> | -c <ip> | |
| Change port | -p 5202 | -p 5002 | Useful for running several servers |
| Test duration | -t 30 | -t 30 | Seconds. Default 10 |
| Per-interval output | -i 1 | -i 1 | Always use this. The average alone hides everything |
| Parallel streams | -P 8 | -P 8 | One thread each in iperf3 3.16+ |
| Reverse direction | -R | --reverse | iperf2 2.1 and later |
| Both directions at once | --bidir | --full-duplex | Different names, same idea |
| UDP mode | -u | -u | Always pair with -b |
| Target bitrate | -b 500M | -b 500M | iperf3 UDP defaults to 1M without this |
| Skip TCP slow start | -O 2 | --omit 2 | Discards the first N seconds. Matters on short runs |
| JSON output | -J | not available | iperf2 has -e enhanced text instead |
| Server output on client | --get-server-output | n/a | Saves walking to the other machine for UDP results |
| Enhanced reports | n/a | -e | RTT, CWND, in-flight bytes, write timing |
| Latency histograms | n/a | --histograms | The reason to reach for iperf2 on Wi-Fi |
| Responsiveness test | n/a | --bounceback | Round trips per second under load |
| One-way delay | n/a | --trip-times | Requires NTP or PTP synchronised clocks |
| Voice/video pacing | n/a | --isochronous | Frame-paced traffic, log-normal load |
| Restrict who connects | n/a | --permit-key | Use it if you leave a server listening |
| Show version | -v | -v | Do this before comparing any results |
// why wi-fi never hits its phy rate
Half duplex
The medium carries one transmission at a time. Every TCP ACK travelling back competes for airtime with the data going forward. Wired full duplex has no equivalent cost.
Contention overhead
DIFS, random backoff, SIFS and the ACK all consume airtime before and after every frame exchange. At high MCS the payload transmits fast and the fixed overhead dominates.
Aggregation is doing the work
A-MPDU bundles many frames under one Block Ack. Without it, throughput collapses. If you see data frames with no preceding ADDBA exchange, that is your answer.
Rate adaptation moves under you
The MCS changes during the test as the driver reacts to retries. A 60 second run with per-second intervals shows this. A 10 second average hides it.
Airtime is shared
Other clients and neighbouring BSSs on the same channel take airtime from your test. A number measured at 2am is not the number your users get at 10am.
The client is half the system
Driver quality varies enormously between adapters on the same AP, same channel, same distance. Always test more than one client before concluding anything about the infrastructure.
// eight ways to produce a wrong number
01
Testing against a wired host that is slower than the Wi-Fi
A Wi-Fi 6E client on 160 MHz can beat a 1 GbE server port. Your ceiling becomes 940 Mbit/s and you blame the AP. Use 2.5 GbE or better on the server side.
02
Running the server on a phone or a laptop on battery
Power management throttles the CPU and the radio. The result is a measurement of the power profile, not the network.
03
One TCP stream and calling it the ceiling
A single stream is limited by its congestion window and one loss event. Use -P 4 or -P 8 for capacity, -P 1 for a realistic single-application experience. They answer different questions.
04
Forgetting -b on UDP
iperf3 defaults UDP to 1 Mbit/s. You will get a clean 1 Mbit/s result and conclude the link is fine.
05
Comparing a 10 second run to a 10 second run
Wi-Fi throughput is not stationary. Rate adaptation, other clients and DFS events all move it. Run 30 to 60 seconds and report the interval spread, not just the average.
06
Ignoring the last interval
The final second is almost always low because the test is draining. Older guides show 641 Mbit/s and 93.9 Mbit/s final intervals in otherwise clean runs. Read the summary line, not the last interval.
07
Testing only throughput
A saturated link with 400 ms of buffer delay measures as excellent and feels unusable. Run bounceback alongside the throughput test.
08
Assuming the number belongs to the AP
iperf measures the whole path, including the client driver. A well-known 2014 result showed one Intel adapter at under a third of the throughput of its peers on the same AP, purely from driver behaviour.
// field notes
01 iperf3 was single threaded until version 3.16. Before that, parallel streams all shared one core and the tool became the bottleneck on fast links. From 3.16 each -P stream gets its own thread. Check your version with iperf3 -v before trusting a multi-gig result.
02 iperf3 is not officially supported on Windows. The ESnet FAQ says so plainly and recommends iperf2 instead. Every Windows iperf guide you find using port 5001 and -w is running iperf2, whether or not it says so.
03 The -w window flag means different things in the two tools and is usually unnecessary on modern kernels, which auto-tune the receive window. Setting it manually often makes results worse, not better.
04 CVE-2025-54351 is a buffer overflow in the iperf3 --skip-rx-copy zerocopy path, fixed upstream. Do not run an iperf server exposed to an untrusted network, and use --permit-key on iperf2 if you must leave one listening.
05 A Wi-Fi 7 client using MLO spreads traffic across links. A single TCP stream will typically pin to one link and understate the connection. Use -P 4 or higher, then confirm in the capture that more than one link is actually carrying data.
06 Report the interval spread, not just the mean. Two links averaging 400 Mbit/s where one ranges 380-420 and the other 90-700 are not the same link, and only one of them will carry voice.
// share this page
// also on this site
SK
— Shankar K., Wi-Fi engineer, Irving TX
Building WiFi Analyser V2 · CWNA-109 in progress · one post every two weeks
Building WiFi Analyser V2 · CWNA-109 in progress · one post every two weeks
// leave a comment