iperf を活用した測定方法

# はじめに

## 概要・モチベーション
- 通信に関する測定を行う上で必要な iperf3 に関する知識を整理する
- ほかの記事も含めて、通信測定系のTipsとして整理していく(後日リンクを紐づけます)
- 同様の測定を行うたびに、何度も似たような記事を調べている
- 個人的に記事一つですべて済ませられる内容にしたい

:::note info
追加でiperfに関するTips集を公開しました！
https://qiita.com/Nenshu_agetai/items/9ab65a5e0ed3cc94b669
:::

## コンテンツ
- iperfを使用して測定する方法 (有効なオプションなどの紹介)
※ここは他記事の方が詳しいのでリンクを張る形とします。
- どのような測定をしたいか、から使うオプションを決定する

:::note warn
注意
筆者の実務経験に基づき、時に調べて書いていきますが誤りを含む可能性があります。
適宜、指摘いただければ修正します。
:::

## 目次
- iperfビギナー：[まずはiperfを使ってみよう](#iperfの使い方)
- iperfを使いこなしたい：測定に合わせたオプションの使い方
    - [UL/DLの使い分け](#測定したいのはdlかulか)
    - [TCP/UDPの使い分け](#測定したいのはtcpかudpか)
    - [書き込みバッファサイズの変更](#あなたが送りたいのはどんなデータ)
- もっと使いこなしたい：測定系Tips(後日、別記事にて更新)
- [おわりに](#おわりに)

# iperfに関する基礎知識

最初に簡単にiperfに関する紹介をします。用途など。

## 通信に関する測定指標
通信に関する試験・測定を行うとき、必ず出てくる通信品質指標が3つあります。

- 通信帯域 (スループット)
- ロス率
- 遅延

通信品質を図るとき、通信が間に入るシステムの評価を行うとき、など
様々な場面で上記の測定が求められる。

## iperfとはなにか？

上記の3指標のうち、特に**通信帯域**を測定できるツールが iperfです。(UDPではロス率も測れる)
以下にgithubを引用する。ライセンスはBSDライセンスとのこと(2023年6月現在)。

https://github.com/apietila/iperf3

# iperfの使い方

用途別にiperfの使い方を記載します。

## インストール

### Linux(Ubuntu)
まずインストールする。Ubuntuの場合は以下でインストールする。
```bash
sudo apt install iperf3
```
Linuxはコマンドで使用できる。
```bash
iperf3 -s
```
### Windows

Windowsの場合は以下からダウンロードする。

https://iperf.fr/iperf-download.php

使う場合はダウンロードしたファイルを展開し、フォルダに含まれる iperf3.exe を使用する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611862/90f7fcb7-f396-9b61-e009-3930a3dedbb0.png)

```powershell
cd path/to/iperf3
iperf3.exe -s
```

## まずは測定してみる

iperfはクライアントとサーバが存在します。
どうやら制御用のコネクションと、データ通信用のコネクションを張っている様子です。

```plantuml
Client->Server: Control Port Open (TCP 3way Handshake)
Client<-Server: OK
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
```

なお本記事では、ClientからServerへの通信をUL(UpLink)とし、
ServerからClientへの通信をDL(DownLink)とします。

:::note info
余談(イメージを持ってもらうために)
一般的なネットワークではServerはAWSなど、固定された場所にいます。
Clientはスマホや、PCなど現実世界で色々な場所を動き回るものが該当します。
また、UpLinkとDownLink はDownLinkの方が通信速度が早い場合がほとんどです。
モバイルネットワークでも、一部の固定回線でもUpLinkは遅いです。
:::


### サーバ側
測定には先にサーバ側の立ち上げが必要です。(-sオプション)

```bash
iperf3 -s
```

### クライアント側
クライアントは、サーバを指定する必要があります。
```bash
iperf3 -c localhost
```

### 結果
途中で終わるには Ctrl+c を押します。
```
Connecting to host localhost, port 5201
[  4] local ::1 port 63913 connected to ::1 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec   876 MBytes  7.35 Gbits/sec
[  4]   1.00-2.00   sec   845 MBytes  7.09 Gbits/sec
[  4]   2.00-3.00   sec   911 MBytes  7.64 Gbits/sec
[  4]   3.00-4.00   sec   706 MBytes  5.92 Gbits/sec
[  4]   4.00-4.38   sec   291 MBytes  6.40 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-4.38   sec  3.54 GBytes  6.95 Gbits/sec                  sender
[  4]   0.00-4.38   sec  0.00 Bytes  0.00 bits/sec                  receiver
iperf3: interrupt - the client has terminated
```

# 色々なオプションを試す

iperfには様々なオプションがあります。以下の記事が非常にわかりやすくまとめてくれています。
ここでは測定時の用途などに合わせてオプションを組み合わせる方法を説明します。

https://qiita.com/yokoc1322/items/bfd8b6e69d6bdb3bb1c6

## 測定したいのはDLかULか？

通信の方向は良く意識してください。
クライアントからサーバへのアクセスなのか、サーバがデータを配布する通信なのか。
DLを指定する`-R`オプションコマンドにより、クライアント側で切り替えます。

### ULの場合(オプション不要)
```
iperf -c localhost
```

### DLの場合
```
iperf -c localhost -R
```

## 測定したいのはTCPかUDPか？

TCP/UDPどちらのスループットを測りたいか良く考えてください。
クライアントのコマンドで使い分けをします。

### TCPの場合
普通にコマンドを打てばOKです。
```bash
iperf3 -c localhost
```

### UDPの場合

特にUDPの場合は以下の点に注意が必要です。

- UDPはデフォルトだと帯域が1Mbpsに制限されます。
- ULの通信では、Client側に詳細な記録が残りません。

対策として、以下を実施します。

- `-b`オプションで帯域指定する
- `--get-server-output`オプションでサーバの結果を受信する

```bash
iperf3 -t 10 -u -c localhost -b 20M --get-server-output
```
※ `-t 10`は 10秒だけ実行の意味

例えば、帯域が10Mbpsしかないときに20Mbpsを送るとどうなるか。(以下、結果)
```
Connecting to host localhost, port 5201
[  5] local 127.0.0.1 port 35494 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Total Datagrams
[  5]   0.00-1.00   sec  2.40 MBytes  20.1 Mbits/sec  77  
[  5]   1.00-2.00   sec  2.37 MBytes  19.9 Mbits/sec  76  
[  5]   2.00-3.00   sec  2.40 MBytes  20.2 Mbits/sec  77  
[  5]   3.00-4.00   sec  2.37 MBytes  19.8 Mbits/sec  76  
[  5]   4.00-5.00   sec  2.37 MBytes  20.0 Mbits/sec  76  
[  5]   5.00-6.00   sec  2.40 MBytes  20.2 Mbits/sec  77  
[  5]   6.00-7.00   sec  2.37 MBytes  19.8 Mbits/sec  76  
[  5]   7.00-8.00   sec  2.37 MBytes  20.0 Mbits/sec  76  
[  5]   8.00-9.00   sec  2.37 MBytes  19.9 Mbits/sec  76  
[  5]   9.00-10.00  sec  2.40 MBytes  20.2 Mbits/sec  77  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-10.00  sec  23.9 MBytes  20.0 Mbits/sec  0.000 ms  0/764 (0%)  sender
[  5]   0.00-20.09  sec  23.8 MBytes  9.95 Mbits/sec  13.813 ms  0/763 (0%)  receiver

Server output:
Accepted connection from 127.0.0.1, port 49420
[  5] local 127.0.0.1 port 5201 connected to 127.0.0.1 port 35494
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-1.00   sec  1.12 MBytes  9.43 Mbits/sec  13.031 ms  0/36 (0%)  
[  5]   1.00-2.00   sec  1.19 MBytes  9.95 Mbits/sec  13.256 ms  0/38 (0%)  
[  5]   2.00-3.00   sec  1.19 MBytes  9.95 Mbits/sec  14.402 ms  0/38 (0%)  
[  5]   3.00-4.00   sec  1.19 MBytes  9.93 Mbits/sec  13.374 ms  0/38 (0%)  
[  5]   4.00-5.00   sec  1.19 MBytes  9.96 Mbits/sec  13.125 ms  0/38 (0%)  
[  5]   5.00-6.00   sec  1.19 MBytes  9.97 Mbits/sec  14.048 ms  0/38 (0%)  
[  5]   6.00-7.00   sec  1.22 MBytes  10.2 Mbits/sec  15.028 ms  0/39 (0%)  
[  5]   7.00-8.00   sec  1.19 MBytes  9.95 Mbits/sec  13.543 ms  0/38 (0%)  
[  5]   8.00-9.01   sec  1.19 MBytes  9.88 Mbits/sec  16.156 ms  0/38 (0%)  
[  5]   9.01-10.00  sec  1.19 MBytes  10.0 Mbits/sec  13.667 ms  0/38 (0%)  
[  5]  10.00-11.02  sec  1.19 MBytes  9.78 Mbits/sec  18.627 ms  0/38 (0%)  
[  5]  11.02-12.02  sec  1.22 MBytes  10.2 Mbits/sec  16.537 ms  0/39 (0%)  
[  5]  12.02-13.01  sec  1.19 MBytes  10.1 Mbits/sec  19.614 ms  0/38 (0%)  
[  5]  13.01-14.00  sec  1.19 MBytes  10.0 Mbits/sec  16.436 ms  0/38 (0%)  
[  5]  14.00-15.05  sec  1.16 MBytes  9.29 Mbits/sec  17.400 ms  0/37 (0%)  
[  5]  15.05-16.01  sec  1.22 MBytes  10.7 Mbits/sec  19.942 ms  0/39 (0%)  
[  5]  16.01-17.00  sec  1.19 MBytes  9.99 Mbits/sec  17.981 ms  0/38 (0%)  
[  5]  17.00-18.00  sec  1.16 MBytes  9.71 Mbits/sec  19.368 ms  0/37 (0%)  
[  5]  18.00-19.00  sec  1.22 MBytes  10.2 Mbits/sec  19.063 ms  0/39 (0%)  
[  5]  19.00-20.00  sec  1.19 MBytes  9.97 Mbits/sec  13.406 ms  0/38 (0%)  
[  5]  20.00-20.09  sec  95.9 KBytes  9.17 Mbits/sec  13.813 ms  0/3 (0%)  
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Jitter    Lost/Total Datagrams
[  5]   0.00-20.09  sec  23.8 MBytes  9.95 Mbits/sec  13.813 ms  0/763 (0%)  receiver
```

クライアント側は20Mbpsで送り続けますが、受信側は10Mずつしか受け取れません。
**送信側(Client)は20Mbpsだと結果を出し、受信側(Server)は10Mbpsだと結果を出します**。
正しいのはサーバ側です。

これはUDPが到達確認を行わないため、送った側は送ったデータ量しか見れない事で発生します。
**UDPは常に受け取る側のログを取る必要があることに注意してください**。

## あなたが送りたいのはどんなデータ？

ここまでで、とりあえず測定はできるようになりました。
だけど、実際に測定をするときにはアプリケーションを意識する必要があります。

例えば、以下みたいな感覚です。
- サーバからファイルを配る(DL/TCP/データ大)
- IoTでクライアントからサーバへ小さなパケットを定期送信する(UL/TCP/データ小)
- 現場からサーバへリアルタイムに映像伝送する(UL/UDP/データ大)

あえて書きましたが、UL/DLとTCP/UDPのほかにデータの大きさがパラメータとして考えられます。
このデータの大きさを現実に近づけるオプションが `-l` オプションです。

iperf通信は以下の形式で実施していると記載しました。
もうちょっと詳しく書くと…

```plantuml
Client->Server: Control Port Open (TCP 3way Handshake)
Client<-Server: OK
Client<-Client: Set Data
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
Client<-Client: Set Data
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
Client<->Server: Sending Measurmeant Data...
```

iperfは、上記の `Sending Measurmeant Data...` において固定の長さのデータを送り、
一定期間内に届いたパケット数などを用いて帯域を推定します。

この送信データ長に関しては、IP/TCPのパケット構造上で最大値が決まっていますが、
上記の `Set Data` と書いた部分におけるバッファへの書き込み量については
`-l`オプションで変えることができます。

実際の通信でも、1MBのファイルを送る場合は1MBのデータがバッファに書き込まれます。
これをコマンド上で再現することができる、ということです。

### `-l` オプションをセットしない場合
デフォルトはTCPで128KB、UDPで8KBらしい。
```bash
iperf3 -t 3 -c localhost
```

```
Connecting to host localhost, port 5201
[  5] local 127.0.0.1 port 37222 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  3.37 GBytes  28.9 Gbits/sec    0   2.94 MBytes       
[  5]   1.00-2.00   sec  2.91 GBytes  25.0 Gbits/sec    0   3.12 MBytes       
[  5]   2.00-3.00   sec  3.04 GBytes  26.1 Gbits/sec    0   3.12 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-3.00   sec  9.31 GBytes  26.7 Gbits/sec    0             sender
[  5]   0.00-3.04   sec  9.31 GBytes  26.3 Gbits/sec                  receiver
```

### `-l` オプションをセットする場合
最大が1024K (1Mも可) まで。それ以上はエラーが出ます。
例えば1KBでセットしてみると。
```bash
iperf3 -t 3 -c localhost -l 1K
```
クライアントの性能にもよりますが、1KB毎にバッファを書くと効率が落ちて、
結果として帯域が狭く感じられます。
```
Connecting to host localhost, port 5201
[  5] local 127.0.0.1 port 55218 connected to 127.0.0.1 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   485 MBytes  4.06 Gbits/sec    0   1.25 MBytes       
[  5]   1.00-2.00   sec   458 MBytes  3.84 Gbits/sec    0   1.25 MBytes       
[  5]   2.00-3.00   sec   437 MBytes  3.67 Gbits/sec    0   1.25 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-3.00   sec  1.35 GBytes  3.86 Gbits/sec    0             sender
[  5]   0.00-3.06   sec  1.34 GBytes  3.76 Gbits/sec                  receiver
```

# iperfに関するTips集
長くなったので別記事にします。~~後日更新予定。~~ 
**2023/12/28 に記事公開しました！**

https://qiita.com/Nenshu_agetai/items/9ab65a5e0ed3cc94b669

# おわりに

自分が働いている時に、いつも同じこと調べているな、
オプションは分かるけど、どういう使い方が良いんだっけ？
みたいなことを感じて書きました。

もうちょっとまとまり良く書きたいですが、難しいですね…。