こんにちは、シニアネットワークエンジニアの私です。
現場でインフラの設計や運用に携わっていると、理屈通りにいかない無線LANの気まぐれに頭を抱えることがよくあります。「電波の強さは十分なはずなのに、なぜかスループットが急降下する」「多数のIoTデバイスが同時にデータを送受信し始めると、パケットロスが爆発的に増える」。そんな泥臭いトラブルの裏で、大抵の場合、目に見えない犯人が静かに悪さをしています。その犯人の名前こそが、今回深掘りする「隠れ端末問題(Hidden Terminal Problem)」です。
Web APIの設計やクラウドインフラの構築であれば、ロードバランサーのログを見ればボトルネックは一目瞭然ですが、Wi-Fiという「物理空間を共有する共有媒体(Shared Medium)」の世界では、パケットの衝突(コリジョン)を目視することはできません。今回は、この無線特有の難敵をいかにして調停するか、Wi-Fi規格の裏側で働くRTS/CTS制御のメカニズムと、実務でのパラメータチューニングについて、技術者の視点から徹底的に解説していきましょう。
—
1. 隠れ端末問題とは何か? 物理空間が生む悲劇
まず、なぜ無線空間でパケットが衝突するのか、その構造を頭に叩き込んでおきましょう。
有線LAN(イーサネット)の世界であれば、スイッチングハブや全二重(Full-Duplex)通信の普及により、基本的にパケットの衝突は過去のものとなりました。しかし、Wi-Fiは電波という目に見えない媒体を全方位に向けて放つ「共有メディア」です。基本的にはCSMA/CA(Carrier Sense Multiple Access with Collision Avoidance:キャリア感知多重アクセス/衝突回避)というアルゴリズムをベースに動いています。
通信を行う前に、端末は「今、他の誰かが電波を出していないか(キャリアセンス)」を耳を澄まして確認します。誰も発信していなければ(アイドル状態)、バックオフタイマーを経てデータを送信する。これが基本ルールです。
しかし、ここに「物理的障壁」が立ちはだかります。
[端末A] ------- (厚いコンクリート壁) ------- [アクセスポイント(AP)] ------- [端末B]
上記の配置を想像してください。
端末Aと端末Bは、間に厚いコンクリートの壁を挟んでおり、お互いの電波が全く届きません。- しかし、どちらの端末も中央にある
アクセスポイント(AP)とは通信が可能です。
この状況で何が起きるでしょうか?
端末AがAPに向けて大容量のデータ送信を開始しました。この時、壁の向こう側にいる端末Bは、キャリアセンスを行っても端末Aの電波を検知できません。「お、いま電波は空いているな」と勘違いした端末Bも、同じタイミングでAPに向けて送信を開始してしまいます。
結果どうなるか。APの受信アンテナの真下で、端末Aの電波と端末Bの波形が見事に衝突(コリジョン)を起こし、両方のデータが盛大に破損します。これが、隠れ端末問題の正体です。有線におけるコリジョン検出(CD)が使えない無線LANにおいて、この衝突はリトライの嵐を呼び起こし、レイテンシの増大とスループットの急降下を引き起こす最大の元凶となります。
—
2. RTS/CTSハンドシェイクによる解決フロー
この物理的な見通しの悪さをスマートに解決するために用意された仕組みが、RTS/CTS(Request to Send / Clear to Send)制御です。
これは、データ本体という「重たい荷物」をいきなり送り出す前に、小さな制御フレームを使って周囲のエリアに「今からこのチャネルを占有して通信する宣言」を行うハンドシェイクプロトコルです。実際の通信フローをシーケンスとして追ってみましょう。
端末A アクセスポイント(AP) 端末B
| | |
|--- [1] RTS (送信要求) ------>| |
| (我が輩はこれから送信する)| |
| |-- [2] CTS (送信許可) -->|
| | (周囲よ、一定時間静粛に!)|
|<-- [2] CTS (送信許可) -------| |
| | | (端末BはCTSを受信し、
| | | タイマー期間中は送信を控える)
| | |
|=== [3] DATA送信 ============>| |
| | |
|<-- [4] ACK (確認応答) -------| |
| | |
このフローの巧妙な点は、APが発信するCTSフレームにあります。
端末AからのRTSを受けたAPは、周囲に向けてCTSをブロードキャスト(あるいは返信)します。このCTSフレームには、「これからどれくらいの時間(Duration)、このチャネルを占有するか」という情報が記載されています。
先の例で言えば、壁の向こう側にいる端末Bには端末Aの電波は届きませんが、APからのCTS電波は届く位置にいます。端末Bは、APから送られてきたCTSを受け取ることで、「おっと、今からこのエリアは通信が立て込むから、俺の送信はしばらく待機(Network Allocation Vector: NAVを設定して沈黙)しよう」と事前に察知できるわけです。これで隠れ端末同士の衝突を綺麗に回避できます。
—
3. パラメータチューニングと「RTS Threshold」の現実
「それなら、常にすべてのパケットでRTS/CTSを使えば完璧じゃないか!」と思われるかもしれませんが、エンジニアの世界にフリーランチはありません。
RTS/CTSは、本命のデータを送る前に必ず小さなハンドシェイクを挟むため、オーバーヘッド(通信の無駄)が発生します。小まめに数バイトの制御フレームをやり取りする分、理想的な最大スループットはわずかに低下します。そのため、無線ルーターや企業向けのAP(Cisco, Aruba, Ubiquitiなど)には、RTS Threshold(RTS閾値)という設定項目が用意されています。
RTS Thresholdの仕組み
- 設定値(例:
2346バイト): 送信しようとするパケットのサイズがこの閾値未満の場合は、RTS/CTSを行わずに直接データを送信します(デフォルト値として最大値の2346に設定されていることが多いです)。 - 閾値を下げる(例:
512バイト): 512バイトを超えるパケットを送る際は、すべてRTS/CTSを強制します。オフィスや工場などで端末が多く、隠れ端末問題によるパケットロスが疑われる場合にこの値を小さく(シビアに)調整します。
実務での判断基準
現代のWi-Fi 6 (802.11ax) や最新のWi-Fi 7 (802.11be) では、OFDMAやBSS Coloringといった強力な干渉制御技術が導入されていますが、それでも物理的な遮蔽物による隠れ端末問題を完全にゼロにすることはできません。
もしあなたがインフラエンジニアとして現場で無線LANの設計・保守を行っており、以下のような症状に直面した場合は、RTS Thresholdの調整を検討すべきです。
1. 特定のフロアやコンクリート壁の死角にある端末だけ、Pingの応答速度(レイテンシ)が不規則に跳ね上がる。
2. 同時接続数が上がると、特定のAP配下でTCPの再送率(Retransmission Rate)が跳ね上がる。
—
4. 実務で役立つ設定と診断のTips
ここからは、より実践的なアプローチとして、ネットワーク機器の設定ファイルの例や、現場で使える診断用のコマンドライン操作を見ていきましょう。
1. 無線コントローラー / APの設定例 (概念的なYAML設定)
多くのエンタープライズ向けAPやオープンソースのファームウェア(OpenWrtなど)では、無線インターフェースの詳細パラメータとしてRTS閾値を指定できます。
# 企業向け無線アクセスポイントの共通無線設定サンプル (Wi-Fi 6 / 5GHz帯)
wireless_interface:
band: "5GHz"
channel: 36
channel_width: "80`MHz"
# 隠れ端末対策のためのRTS/CTS制御パラメータ
rts_threshold: 512 # デフォルトの2346から512バイトに引き下げ、オーバーヘッドを許容してでも衝突を回避
# その他の干渉軽減パラメータ
short_guard_interval: true
aggregation: "AMPDU" # パケット集合化による効率化
- エンジニアの現場メモ:闇雲にRTS閾値を極端に小さく(例えば
1や64などに)すると、制御パケットの割合が激増し、逆に全体のスループットが致命的に低下します。パケットキャプチャツール(Wiresharkなど)を用いて、無線区間の再送フレームの割合を確認しながら、512や1024といった値を段階的にテストするのが鉄則です。
2. 現場でのトラブルシューティング:Pythonを用いたAPI監視とレイテンシ測定
無線区間の不安定さは、上位レイヤーのアプリケーション(Web API等)のタイムアウトとして現れます。Wi-Fiの不安定さが原因でAPIリクエストがドロップしていないかを検証するための、シンプルな死活監視・レイテンシ測定スクリプト(Python)の例を記載します。
import time
import requests
from requests.exceptions import RequestException
# 監視対象のローカルAPIエンドポイント
TARGET_API_URL = "http://192.168.1.100:8080/api/v1/health"
TIMEOUT_SEC = 2.0
INTERVAL_SEC = 1.0
def monitor_wireless_api():
print(f"[*] 無線環境経由のAPI監視を開始します: {TARGET_API_URL}")
print("[*] Ctrl+C で停止します。\n")
consecutive_failures = 0
try:
while True:
start_time = time.time()
try:
response = requests.get(TARGET_API_URL, timeout=TIMEOUT_SEC)
elapsed_ms = (time.time() - start_time) * 1000
if response.status_code == 200:
print(f"[SUCCESS] Status: {response.status_code} | Latency: {elapsed_ms:.2f} ms")
consecutive_failures = 0
else:
print(f"[WARNING] 異常なステータスコードを検出: {response.status_code}")
except RequestException as e:
elapsed_ms = (time.time() - start_time) * 1000
consecutive_failures += 1
print(f"[ERROR] タイムアウト/通信エラー ({elapsed_ms:.2f} ms): {e}")
# 連続エラーが一定数を超えた場合、無線パケットロス(隠れ端末の可能性)を疑うログを出す
if consecutive_failures >= 3:
print(" >>> 警告: 連続して通信断が発生しています。無線空間のコリジョンや電波干渉を疑ってください。")
time.sleep(INTERVAL_SEC)
except KeyboardInterrupt:
print("\n[*] 監視を終了しました。")
if __name__ == "__main__":
monitor_wireless_api()
このようなスクリプトを手元のクライアントPC(無線接続)で走らせ、特定の時間帯や負荷時にだけレイテンシがスパイクしたり、タイムアウトが多発したりする場合は、上位のネットワーク障害ではなく、無線特有の隠れ端末問題や電波干渉が原因である確率がグッと高まります。
—
5. まとめ
今回は、Wi-Fiネットワークの裏側で静かにエンジニアを悩ませる「隠れ端末問題」と、それを救う「RTS/CTS制御」の仕組みについて解説しました。
- 物理的な見通しの悪さが、お互いの電波を感知できない端末を生み出し、パケットの衝突を引き起こす。
- RTS/CTSハンドシェイクは、小さな制御フレームのやり取りとNAV(ネットワーク配分ベクトル)によって、周囲の端末に一時的な沈黙を命じ、衝突を未然に防ぐ。
- ただし、すべての通信に適用するとオーバーヘッドになるため、現場の環境(遮蔽物の多さや端末密度)に合わせて
RTS Thresholdを適切にチューニングすることが重要。
インフラやアプリケーションの設計において、目に見えない「無線レイヤー」の挙動を想像できるかどうかが、プロのネットワークエンジニアとしての腕の見所です。現場で不可解なパケットロスに直面したときは、ぜひ今回のRTS/CTSの挙動やパラメータ調整を思い出してみてください。あなたのトラブルシューティングの引き出しの一つとして役立てば幸いです。
コメント