// 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.
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
PlatformCommandNote
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.
// 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 areUseWhy
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 wantiperf3iperf2Notes
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
rf
RSSI, SNR, MCS & Link Budget
calc
MCS Rate Calculator
reference
How Engineers Troubleshoot
← previous
reference
Tools Comparison
next →
tool
Exchange Timing & SLA
SK
Shankar K., Wi-Fi engineer, Irving TX
Building WiFi Analyser V2 · CWNA-109 in progress · one post every two weeks
// leave a comment