0

This is a follow-on to: Getting realtime output using subprocess

I'm trying to use subprocess to capture output from iperf3 in real time (using python 3.6 on windows). The goal is to leave the iperf3 session running continuously and grab the data to update a real time plot.

I created an implementation based on the referenced question (see code at end of post), but the code still waits on the first "readline" call for the iperf3 session to complete.

Output and desired behavior

My code returns the output:

Iperf test
Popen returns after: 0.012966156005859375 seconds
Readline 0 returned after: 3.2275266647338867 seconds, line was: Connecting to host 10.99.99.21, port 5201
Readline 1 returned after: 3.2275266647338867 seconds, line was: [  4] local 10.99.99.7 port 55563 connected to 10.99.99.21 port 5201
Readline 2 returned after: 3.2275266647338867 seconds, line was: [ ID] Interval           Transfer     Bandwidth
Readline 3 returned after: 3.2275266647338867 seconds, line was: [  4]   0.00-0.50   sec  27.4 MBytes   458 Mbits/sec
Readline 4 returned after: 3.2275266647338867 seconds, line was: [  4]   0.50-1.00   sec  29.0 MBytes   486 Mbits/sec
Exited

The outputs show that the first readline call doesn't return until after 3 seconds, when the iperf session completes. The desired behavior is that the readline calls 0, 1, and 2 return almost immediately, and readline call #3 returns after approx. 0.5 seconds, as soon as iperf3 has completed the first 0.5 second reporting interval.

Code

import subprocess
import time

if __name__ == "__main__":
    print('Iperf test')
    tref = time.time()

    reads_to_capture = 5
    times = [0] * reads_to_capture
    lines = [''] * reads_to_capture

    interval = 0.5
    ip = '10.99.99.21' # Iperf server IP address
    process = subprocess.Popen(f'iperf3 -c {ip} -f m -i {interval} -t 3', encoding = 'utf-8',
            stdout=subprocess.PIPE)

    print(f'Popen returns after: {time.time() - tref} seconds')

    cnt = 0

    while True:
        output = process.stdout.readline()
        if cnt < reads_to_capture: # To avoid flooding the terminal, only print the first 5
            times[cnt] = time.time() - tref
            lines[cnt] = output
            cnt = cnt + 1
        if output == '': 
            rc = process.poll()
            if rc is not None:
                break

    rc = process.poll()

    for ii in range(reads_to_capture):
        print(f'Readline {ii} returned after: {times[ii]} seconds, line was: {lines[ii].strip()}')

    print('Exited')
Selvek
  • 130
  • 1
  • 9

2 Answers2

0

Sorry for my late answer. There is an API for Iperf3, luckily this comes with the standard iperf3 build/installation.

This API allows python to take the common output of iperf3.

I let you the official website of the python wrapper for iperf3. It comes with simple examples for your use. Hope I could have gave you an answer.

https://iperf3-python.readthedocs.io/en/latest/index.html

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 27 '21 at 16:47
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/30682544) – Flair Dec 27 '21 at 20:32
0

In order to get the real time output from iperf3 to the subprocess.Popen you need the --forceflush flag in the iperf3 command. The --forceflush flag is introduced in iperf 3.1.5, unfortunately the official compiled iperf3.exe file have only until iperf 3.1.3.

Two solution for you,

  1. get the iperf >= 3.1.5 from non official route like: https://files.budman.pw/
  2. use linux's iperf3

Attached with my code:

import subprocess

my_iperf_process = subprocess.Popen(["iperf3","-c","192.168.0.1","--forceflush"],stdout=subprocess.PIPE)

for line in my_iperf_process.stdout:
    print(line)

The help message of --forceflush:

--forceflush    force flushing output at every interval