もがもがしいSEブログ

もがもがしく生きる

Tropo API を利用した自動音声応答装置(IVR)の構築サンプル

あけましておめでとうございます。@moga_shiです。
去年は初配属の年で、あまり業務外のことに時間を割けなかったのですが、最近社内失職気味に
なってきたので、やる気さえあればいろいろ活動できそうな感じです。やる気さえあれば。
というわけで新年最初の記事です。クラウド電話APITropo』をご紹介します。

はじめに

クラウド電話APIといえば『Twilio』を思い浮かべる人は多いのではないでしょうか。
率直に言って、国内でクラウド電話APIを利用したサービスを開発したいのであれば、
日本語ドキュメントやサポートが充実している Twilio を利用するのが現状では最適解だと思います。
ただ、Twilio を検証目的で利用する場合、以下の点で少し困ります。

  • 月額基本料金490円 + 従量料金がかかる
  • 無料のトライアルアカウントだと冒頭でデモ用メッセージが流れる

まぁ、月々にわずかな金額を支払えば済む話ではあるのですが、検証用に複数の電話番号を使いたい時や、
お遊びで一瞬だけ公開するアプリを作りたい時には少々困った話になります。
そんな時に役に立つのが Tropo です。
トライアルの Development Use に限り、基本料金や発着信の料金がかかりません。*1*2
電話番号は、東京の03番号を始めとする数十カ国の番号を5つ程度まで持つことができます。*3


使い方

開発するサービスへの接続方法により、Scripting API と Web API の2通りの使い方があります。
前者がホスティング、後者が自前のサーバを使う方法ですが、図で描くと以下のようになります。

  • Scripting API

f:id:moga_shi:20140106231841p:plain

f:id:moga_shi:20140106231848p:plain
データベースや他サービスと連携したい場合は Web API の方を使うことが多くなると思いますので
以下のサンプルでは Web API を利用した事例で説明します。

サンプル

運送会社の再配達受付サービスを考えます。
荷物の配達時に家の人が不在だった場合、不在票を入れておき、後日そこに記載されている番号に
電話をかけると、伝票番号や再配達日時を入力するように求められ・・・というおなじみのシステムです。

サンプルコードは PHP ですが、他に Ruby, Node.js, Python が使えます。
コードがアレなのですが、いい書き方があればご教示ください。

[redeliver/config.php]

<?php
$DSN = array(
        'phptype' => 'mysql',
        'username' => 'YOUR_USERNAME',
        'password' => 'YOUR_PASSWORD',
        'hostspec' => 'localhost',
        'database' => 'YOUR_DATABASE',
);
$BASE_URL = 'http://[YOUR_SERVER]/redeliver/';
$VOICE_URL = $BASE_URL . 'voice/';    // 音声ディレクトリ(音声は CeVIO Creative Studio で作成)
?>


[redeliver/index.php]

<?php
require_once 'MDB2.php';
require_once 'tropo.class.php';    // https://github.com/tropo/tropo-webapi-php
require_once 'lib/limonade.php';    // http://limonade-php.github.io/

/**
 * 初回アクセス
 */
dispatch_post('/welcome', 'app_welcome');
function app_welcome() {

        require_once 'config.php';

        $tropo = new Tropo();
        $session = new Session();

        $from = $session->getFrom();
        $callerID = $from["id"];

        ### Connect to Database ###
        $mdb2 =& MDB2::connect($DSN);
        if (PEAR::isError($mdb2)) {
                die($mdb2->getMessage());
        }

        ### Access History ###
        $res =& $mdb2->exec("INSERT INTO history (tel_no) VALUES ('$callerID')");
        if (PEAR::isError($res)) {
                die($mdb2->getMessage());
        }

        $tropo->say($VOICE_URL . "welcome.mp3");    // "こちらは再配達受付センターです。これから流れるメッセージに従って入力してください。"
        $tropo->on(array("event" => "continue", "next" => "index.php?uri=slip_register"));

        $tropo->RenderJson();
}

/**
 * 伝票番号入力
 */
dispatch_post('/slip_register', 'app_slip_register');
function app_slip_register() {

        require_once 'config.php';

        $tropo = new Tropo();

        $options = array("choices" => "[12 DIGITS]", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        $tropo->ask($VOICE_URL . "slip_register.mp3", $options);    // "伝票番号12桁を入力してください。" ※0~9の12桁の数字なら何でも OK にしてます。
        $tropo->on(array("event" => "continue", "next" => "index.php?uri=slip_confirm"));

        $tropo->RenderJson();
}

/**
 * 伝票番号確認
 */
dispatch_post('/slip_confirm', 'app_slip_confirm');
function app_slip_confirm() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        $slip_no = $result->getValue();
        $options = array("choices" => "1,3", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        $tropo->say($VOICE_URL . "slip_confirm.mp3");    // "伝票番号は"
        for ($i=0; $i<12; $i++) {
                $tropo->say($VOICE_URL . "0" . substr($slip_no, $i, 1) . ".mp3");
        }
        $tropo->ask($VOICE_URL . "confirm.mp3", $options);      // "ですね? よろしければ 1 を、訂正する場合は 3 を入力してください。"
        $tropo->on(array("event" => "continue", "next" => "index.php?uri=slip_search&slip_no=$slip_no"));

        $tropo->RenderJson();
}

/**
 * 伝票番号検索(未実装)
 */
dispatch_post('/slip_search', 'app_slip_search');
function app_slip_search() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        $choice = $result->getValue();
        $slip_no = $_GET['slip_no'];

        switch ($choice) {
                case "1":
                        /**
                         * 本当ならここで伝票番号が正しいか判定する
                         */
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_register&slip_no=$slip_no"));
                        break;
                case "3":
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=slip_register"));
                        break;
                default:
                        $tropo->say($VOICE_URL . "wrong.mp3");    // "入力に誤りがあります。"
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=slip_register"));
                        break;
        }

        $tropo->RenderJson();
}

/**
 * 再配達日入力
 */
dispatch_post('/date_register', 'app_date_register');
function app_date_register() {

        require_once 'config.php';

        $tropo = new Tropo();

        $slip_no = $_GET['slip_no'];

        $options = array("choices" => "[4 DIGITS]", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        $tropo->ask($VOICE_URL . "date_register.mp3", $options);    // "再配達の希望日を4桁で入力してください。たとえば、1月23日なら 0123 と入力してください。"
        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_confirm&slip_no=$slip_no"));

        $tropo->RenderJson();
}

/**
 * 再配達日確認
 */
dispatch_post('/date_confirm', 'app_date_confirm');
function app_date_confirm() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        // 日付チェックは省略...
        $date = $result->getValue();
        $month = substr("$date", 0, 2);
        $day = substr("$date", 2, 2);
        $slip_no = $_GET['slip_no'];
        $options = array("choices" => "1,3", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        $tropo->say($VOICE_URL . "date_confirm.mp3");    // "ご希望の配達日は"
        $tropo->say($VOICE_URL . $month . ".mp3");
        $tropo->say($VOICE_URL . "gatsu.mp3");    // "月"
        $tropo->say($VOICE_URL . $day . ".mp3");
        $tropo->say($VOICE_URL . "nichi.mp3");    // "日"
        $tropo->ask($VOICE_URL . "confirm.mp3", $options);    // "ですね? よろしければ 1 を、訂正する場合は 3 を入力してください。"
        $tropo->on(array("event" => "continue", "next" => "index.php?uri=time_register&slip_no=$slip_no&date=$date"));

        $tropo->RenderJson();
}

/**
 * 再配達時間帯入力
 */
dispatch_post('/time_register', 'app_time_register');
function app_time_register() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        $choice = $result->getValue();
        $slip_no = $_GET['slip_no'];
        $date = $_GET['date'];
        $options = array("choices" => "0,1,2,3,4,5,6", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        switch ($choice) {
                case "1":
                        $tropo->say($VOICE_URL . "time_register01.mp3");    // "再配達の時間帯を入力してください。"
                        $tropo->ask($VOICE_URL . "time_register02.mp3", $options);    // "時間指定がない場合は 0、午前中は 1、12時から14時は 2、14時から16時は 3、16時から18時は 4、18時から20時は 5、20時から21時は 6 を入力してください。"
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=time_confirm&slip_no=$slip_no&date=$date"));
                        break;
                case "3":
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_register&slip_no=$slip_no"));
                        break;
                default:
                        $tropo->say($VOICE_URL . "wrong.mp3");    // "入力に誤りがあります。"
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_register&slip_no=$slip_no"));
                        break;
        }

        $tropo->RenderJson();
}

/**
 * 再配達時間帯確認
 */
dispatch_post('/time_confirm', 'app_time_confirm');
function app_time_confirm() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        // 時間帯チェックは省略...
        $time = $result->getValue();
        $slip_no = $_GET['slip_no'];
        $date = $_GET['date'];
        $month = substr("$date", 0, 2);
        $day = substr("$date", 2, 2);
        $options = array("choices" => "1,3", "name" => "digit", "timeout" => 30, "mode" => "dtmf");

        $tropo->say($VOICE_URL . "time_confirm.mp3");    // "ご希望の配達日時は"
        $tropo->say($VOICE_URL . $month . ".mp3");
        $tropo->say($VOICE_URL . "gatsu.mp3");    // "月"
        $tropo->say($VOICE_URL . $day . ".mp3");
        $tropo->say($VOICE_URL . "nichi.mp3");    // "日"
        $tropo->say($VOICE_URL . "time0" . $time . ".mp3");
        $tropo->ask($VOICE_URL . "confirm.mp3", $options);    // "ですね? よろしければ 1 を、訂正する場合は 3 を入力してください。"

        $tropo->on(array("event" => "continue", "next" => "index.php?uri=complete&slip_no=$slip_no&date=$date&time=$time"));

        $tropo->RenderJson();
}

/**
 * 受付完了
 */
dispatch_post('/complete', 'app_complete');
function app_complete() {

        require_once 'config.php';

        $tropo = new Tropo();
        @$result = new Result();

        $choice = $result->getValue();
        $slip_no = $_GET['slip_no'];
        $date = $_GET['date'];
        $time = $_GET['time'];

        switch ($choice) {
                case "1":
                        /**
                         * 本当ならここでデータベースを更新する
                         */
                        $tropo->say($VOICE_URL . "complete.mp3");    // "再配達のご依頼を承りました。ありがとうございました。"
                        break;

                case "3":
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_register&slip_no=$slip_no"));
                        break;
                default:
                        $tropo->say($VOICE_URL . "wrong.mp3");    // "入力に誤りがあります。"
                        $tropo->on(array("event" => "continue", "next" => "index.php?uri=date_register&slip_no=$slip_no"));
                        break;
        }

        $tropo->RenderJson();
}

run();
?>


以上のサンプルコードを実装した電話番号が 03-4578-2345 です。よろしければお試しください。

*1:もっとも、電話回線の料金は Tropo が負担しているため、検証が終わり次第 Production Account へ移行するか番号をリリースするのが筋なのですが。

*2:アウトバウンドコールにはアカウントの認証が必要です。https://www.tropo.com/docs/webapi/quickstarts/making-call

*3:今のところ明確な上限はないようです。9つまで電話番号を増やしたところ、検証目的を説明するか番号をリリースしてくださいとの連絡を受けました。

インバウンドトラフィック用にプロバイダを追加契約した

f:id:moga_shi:20131005041237p:plain

現状のネットワークこんな感じだけど以下の問題があった。

  • メインWANルータ(WZR-1750DHP)にヘアピンNAT機能がなく、iPhoneテザリング等を使わないとグローバルからの見え方が分からない。
  • WAN回線が固定IPではないため、PPPoEのセッションが切断された際にDDNSへの再登録が必要になり復旧時間が長くなる。また、VPNの接続形態に制約が生じる。
  • 検証目的でネットワーク構成を変更した際、設定ミスによりサーバへのアクセスができなくなる可能性がある。

そこで固定IP1で料金の安いグリーンネットというISPと契約した。
今はBフレでもPPPoE 2セッション貼れるので最高便利。


変更後の構成は以下の通り。

f:id:moga_shi:20131005041241p:plain

インターネットアクセス用LAN(Guest-LAN)の構成はそのままに、インバウンドの方は
ゲートウェイにFG80Cを設置して上質なエンタープライズ感を醸し出す。


試しにさくらVPSで iperf -s して、そこまでの帯域を測定した。

[root@localhost ~]# iperf -s -i 1 -t 10
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[  4] local xxx.xxx.xxx.xxx port 5001 connected with xxx.xxx.xxx.xxx port xxxx  ;既存回線(Biglobe)
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0- 1.0 sec  9.54 MBytes  80.0 Mbits/sec
[  4]  1.0- 2.0 sec  10.5 MBytes  88.0 Mbits/sec
[  4]  2.0- 3.0 sec  10.6 MBytes  88.8 Mbits/sec
[  4]  3.0- 4.0 sec  10.6 MBytes  88.9 Mbits/sec
[  4]  4.0- 5.0 sec  10.7 MBytes  89.5 Mbits/sec
[  4]  5.0- 6.0 sec  10.7 MBytes  89.6 Mbits/sec
[  4]  6.0- 7.0 sec  10.7 MBytes  90.0 Mbits/sec
[  4]  7.0- 8.0 sec  10.6 MBytes  88.8 Mbits/sec
[  4]  8.0- 9.0 sec  10.7 MBytes  89.9 Mbits/sec
[  4]  9.0-10.0 sec  10.7 MBytes  89.6 Mbits/sec
[  4]  0.0-10.0 sec   106 MBytes  88.3 Mbits/sec
[  4] local yyy.yyy.yyy.yyy port 5001 connected with yyy.yyy.yyy.yyy port xxxx  ;新規回線(グリーンネット)
[  4]  0.0- 1.0 sec  8.58 MBytes  72.0 Mbits/sec
[  4]  1.0- 2.0 sec  10.4 MBytes  87.2 Mbits/sec
[  4]  2.0- 3.0 sec  10.4 MBytes  87.0 Mbits/sec
[  4]  3.0- 4.0 sec  10.3 MBytes  86.5 Mbits/sec
[  4]  4.0- 5.0 sec  10.5 MBytes  88.1 Mbits/sec
[  4]  5.0- 6.0 sec  10.4 MBytes  86.8 Mbits/sec
[  4]  6.0- 7.0 sec  10.3 MBytes  86.2 Mbits/sec
[  4]  7.0- 8.0 sec  10.4 MBytes  86.9 Mbits/sec
[  4]  8.0- 9.0 sec  10.3 MBytes  86.2 Mbits/sec
[  4]  9.0-10.0 sec  10.4 MBytes  87.1 Mbits/sec
[  4]  0.0-10.0 sec   102 MBytes  85.4 Mbits/sec

既存回線の方が若干良い値が出ているが、帯域共用ユーザーの数や
iperf -s を実行するサーバのあるISPまでの距離の影響を受けるため、あくまで参考程度。

きんモザのアリスのgif画像を取得するAPIを作った

きんモザが終わる。

毎週楽しみにしていたきんモザが。

寂しい。

アリスと会えなくなることが、寂しい。

寂しいので、アリスのgif画像を取得できるAPIを作った。

http://mogashi.com/alice/

クエリストリング無しでアクセスするとランダムで Content-Type: image/gif な画像が降ってくる。

クエリストリングに使える有効なパラメータは以下の通り。

パラメータ説明
id画像を一意に識別する ID。これを指定すると ep, no の指定は無効化される。
ep話数。ep=1 なら第1話。no と一緒に指定して使う。なお作者は第6話のたこ焼きのシーンを作成して満足したため第7話以降のgif画像は存在しない。
10/5追記:11話のアリスの泣き顔に心を打たれたためgif画像を追加した。ついでに12話まで作成した。
no話数の中の順番。no=1 に近いほど序盤のシーンになり、数が大きくなると終盤のシーンになる。たとえば ep=4&no=6 とかにすると「わたし、何もあげられるものないから、歌をうたうよ!」のシーンになる。かわいい。


そういえば ThinkPad 55Y9024 キーボードのQキーが購入後4ヶ月でぶっ壊れた。エンタープライズ、案外脆い。





Raspberry Pi でネットワーク監視ガジェットを作った

会社に置いてみたけど反応もらえなくて悲しかったのでインターネットに書く。

ネットワーク監視ガジェット スラ子ちゃん - もがもがしいブース | makebooth

そんなことより Gifzo べんり!!!!

続・IEEE802.11ac Draft版準拠ルータ WZR-1750DHPの通信速度をテストする

前回の記事
IEEE802.11ac Draft版準拠ルータ WZR-1750DHPの通信速度をテストしたかった - もがもがしいSEブログ
があまりにアレだったので、どうしたものかと考えあぐねていたところ、
@caplioさんからiperfというツールを勧められたので試してみました。
調べたところ、iperfはメモリ間の転送速度を計測するので、HDDがボトルネックになることはないようです。

新しい計測方法は以下の通り。

  • iperfでTCP/UDPのパケットを10秒間×5回送信し、受信側の速度を測定。


PCのスペックは前回と同じですが、一応再掲。

  • 左側…自作PCCore i5 3570, DDR3-1600 8GBx2)
  • 右側…Let's Note CF-S10(Core i5 2540M, DDR3-1066 4GB)

自作PCのアイコンがノートPCになってるのは気にしないでくださいw


構成1

f:id:moga_shi:20130401004736p:plain

まずは有線接続から。右側のPC(Let's Note)から左側のPC(自作PC)に送信します。

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50662
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-10.0 sec   742 MBytes   622 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50664
[  4]  0.0-10.0 sec   768 MBytes   644 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50665
[  4]  0.0-10.0 sec   791 MBytes   663 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50666
[  4]  0.0-10.0 sec   772 MBytes   647 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50667
[  4]  0.0-10.0 sec   802 MBytes   673 Mbits/sec

$ iperf -s -u
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 52112
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams
[  3]  0.0-10.0 sec  1.25 MBytes  1.05 Mbits/sec   0.001 ms    0/  893 (0%)

TCPで620~670Mbps、UDPで1Mbps。
UDPが異常に遅いですね。
帯域幅のデフォルトが1Mbpsになってるからかなと思って、送信側に -b 2000M のオプションを付けて再計測。


$ iperf -s -u
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 49690
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams
[  3]  0.0-10.0 sec   933 MBytes   783 Mbits/sec   0.060 ms 1769/667521 (0.27%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 49691
[  4]  0.0-10.0 sec   914 MBytes   767 Mbits/sec   0.008 ms 7392/659488 (1.1%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 49692
[  3]  0.0-10.0 sec   944 MBytes   792 Mbits/sec   0.020 ms  999/674085 (0.15%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 49693
[  4]  0.0-10.0 sec   949 MBytes   796 Mbits/sec   0.060 ms  807/678055 (0.12%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 49694
[  3]  0.0-10.0 sec   943 MBytes   791 Mbits/sec   0.059 ms  870/673581 (0.13%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

770~800Mbpsくらいになりました。
それらしい値が出たので次からは同じオプションを使って計測します。


2013/04/10追記:
【清水理史の「イニシャルB」】 NECアクセステクニカ「AtermWG1800HP」VS バッファロー「WZR-1750DHP」 - INTERNET Watch
の記事の計測方法通り、サーバ側で

iperf -s -w <TCPウィンドウサイズ>

クライアント側で

iperf -c <サーバ側IPアドレス> -w <TCPウィンドウサイズ>

を実行してTCPのウィンドウサイズを256KBや4MBに変更したところ、通信速度の計測値が改善しました。
実行結果を載せると長くなるので、記事の最後のグラフに反映します。


構成2

f:id:moga_shi:20130401005058p:plain

右側のPCから左側のPCに、11acで送信します。

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 50616
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-10.0 sec  80.8 MBytes  67.6 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 50617
[  4]  0.0-10.0 sec  90.5 MBytes  75.8 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 50618
[  4]  0.0-10.0 sec  92.4 MBytes  77.4 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 50619
[  4]  0.0-10.0 sec  55.2 MBytes  46.2 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 50620
[  4]  0.0-10.2 sec  92.2 MBytes  75.7 Mbits/sec

$ iperf -s -u
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 56657
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams
[  3]  0.0-10.0 sec   259 MBytes   217 Mbits/sec   0.140 ms  621/185156 (0.34%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 52857
[  4]  0.0-10.0 sec   262 MBytes   220 Mbits/sec   0.127 ms  331/187315 (0.18%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 52858
[  3]  0.0-10.0 sec   259 MBytes   217 Mbits/sec   0.011 ms    0/184873 (0%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 52859
[  4]  0.0-10.0 sec   261 MBytes   219 Mbits/sec   0.217 ms   47/185996 (0.025%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.10 port 52860
[  3]  0.0-10.0 sec   258 MBytes   217 Mbits/sec   0.157 ms  151/184506 (0.082%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

TCPは65~75Mbps、UDPは220Mbpsくらい。


構成3

f:id:moga_shi:20130401010256p:plain

2台のPCをルータに有線で接続し、ルータ同士を2.4GHz + 5GHzの同時使用でつないだ構成。

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50699
[ ID] Interval       Transfer     Bandwidth

[  4]  0.0-10.0 sec   181 MBytes   152 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50700
[  4]  0.0-10.0 sec   181 MBytes   151 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50702
[  4]  0.0-10.0 sec   184 MBytes   154 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50706
[  4]  0.0-10.0 sec   186 MBytes   156 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 50707
[  4]  0.0-10.0 sec   181 MBytes   151 Mbits/sec

$ iperf -s -u
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 61804
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams

[  3]  0.0-10.0 sec   819 MBytes   685 Mbits/sec   0.066 ms 100875/684943 (15%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 61805
[  4]  0.0-10.3 sec   837 MBytes   685 Mbits/sec  13.589 ms 95986/693305 (14%)
[  4]  0.0-10.3 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 61806
[  3]  0.0-10.0 sec   804 MBytes   672 Mbits/sec   0.367 ms 119657/693048 (17%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 61807
[  4]  0.0-10.0 sec   804 MBytes   672 Mbits/sec   0.075 ms 117589/691152 (17%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.99 port 61808
[  3]  0.0-10.0 sec   835 MBytes   698 Mbits/sec   0.110 ms 96913/692708 (14%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

TCPで150Mbps、UDPで670~700Mbps。


構成4

f:id:moga_shi:20130401011201p:plain

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 64.0 KByte (default)
------------------------------------------------------------
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 51116
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-10.0 sec  48.5 MBytes  40.6 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 51117
[  4]  0.0-10.3 sec  30.0 MBytes  24.5 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 51118
[  4]  0.0-10.0 sec  59.4 MBytes  49.7 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 51119
[  4]  0.0-10.0 sec  43.4 MBytes  36.3 Mbits/sec
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 51120
[  4]  0.0-10.0 sec  49.5 MBytes  41.5 Mbits/sec

$ iperf -s -u
------------------------------------------------------------
Server listening on UDP port 5001
Receiving 1470 byte datagrams
UDP buffer size: 64.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 58049
[ ID] Interval       Transfer     Bandwidth        Jitter   Lost/Total Datagrams

[  3]  0.0-10.0 sec   179 MBytes   150 Mbits/sec   0.130 ms    0/127534 (0%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 58050
[  4]  0.0-10.0 sec   175 MBytes   147 Mbits/sec   0.103 ms   20/125107 (0.016%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 58051
[  3]  0.0-10.0 sec   178 MBytes   149 Mbits/sec   0.100 ms    0/127115 (0%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order
[  4] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 58052
[  4]  0.0-10.0 sec   174 MBytes   146 Mbits/sec   0.178 ms   29/124125 (0.023%)
[  4]  0.0-10.0 sec  1 datagrams received out-of-order
[  3] local 192.168.11.9 port 5001 connected with 192.168.11.11 port 58053
[  3]  0.0-10.0 sec   173 MBytes   146 Mbits/sec   0.092 ms    0/123745 (0%)
[  3]  0.0-10.0 sec  1 datagrams received out-of-order

TCPで40Mbps、UDPで150Mbps。
構成2に比べて転送速度が3分の2程度に下がってしまっているのは、ルータをかませたことが原因でしょうか。
壁や天井の影響がそこまでひどくないのであれば、親機に直接つないだ方が速度が出そうです。

ちなみに、壁を1枚挟んだ状態で”左側PC←(有線)←ルータ←(無線11ac)←右側PC”の構成を試したところ、
TCPで45Mbps、UDPで160Mbpsくらいでした。


比較

最後に構成別の測定結果(中央値)をグラフにして掲載します。

f:id:moga_shi:20130410002508p:plain

全体的に、無線を経由すると速度が低下するようですね。
バッファローの測定結果では11acで最低でも500Mbpsは出ていたらしいですが・・・。
ただ、自分の測定方法がいい加減なので、メモリやバスなどの転送速度がボトルネックになっていたり、
ルータの設定方法が甘かったりして、やり方によってはもっと好成績を出せるのかもしれません。

まだひよっこSEなもので、ここをこうしたらいいよ!っていうアドバイスがあればお願いします。
興味を惹くものがあれば、ぜひ人柱としてIYH!!!させていただきますw

2013/04/10追記:
iperfにウィンドウサイズのオプションを追加したところ、計測値が劇的に改善しました。
ファイルの転送速度が、構成2でおよそ130Mbps、構成3でおよそ400Mbpsなので、感覚には割と合っています。
PCとルータ間を無線で接続した時の通信速度低下が気になりますが、今後のファームウェアアップデート等で
改善されていくのかもしれません。

IEEE802.11ac Draft版準拠ルータ WZR-1750DHPの通信速度をテストしたかった

この記事はObsoleteになりました。
続・IEEE802.11ac Draft版準拠ルータ WZR-1750DHPの通信速度をテストする - もがもがしいSEブログ
をご覧ください。

先月1日、総務省によるIEEE802.11acの認可の目処が立ち、
報道を受けた各社が相次いでIEEE802.11ac対応機器の発売を予告しました。
その中の一つ、BUFFALO製のWZR-1750が3/28に発売されまして、
IYHer的衝動を抑えきれずに2台購入したので、通信テストを行ってみました。
(正確には後述の通り、行ってみたかった、ですが……)

構成は4パターンで、速度測定に用いた方法は以下の2通りです。

  • NetMiでTCPパケットを30秒間送受信し、平均送受信速度を測定。
  • 大容量のファイルを送受信し、おおよその平均送受信速度を測定。

なお、使用したPC2台のスペックはこちら。

構成1

f:id:moga_shi:20130401004736p:plain

PC同士を有線LANでつないだだけの構成。
今まで100BASE-TXのルータを使っていたので、どれくらい早くなるかなぁと思って試してみました。

NetMiでは393Mbps、ファイルの送受信はおよそ400Mbpsでした。
普通に800Mbps以上は出ると思っていたので、あれ?という感想です。


構成2

f:id:moga_shi:20130401005058p:plain

一方のPCを有線、もう一方のPCを無線でつないだ構成。

5GHzの時はNetMiで18Mbps, ファイル送受信で140Mbps。
2.4GHzの時はNetMiで22Mbps, ファイル送受信で95Mbps。
この辺りから計測結果の信憑性がまるでダメになってきます。


構成3

f:id:moga_shi:20130401010256p:plain

一方のルータを親機、もう一方のルータを中継機として、PC-ルータ間は有線でつなぐ構成。

NetMiでは31Mbps、ファイルの送受信はおよそ520Mbpsでした。
……何なんでしょう、この差は。


構成4

f:id:moga_shi:20130401011201p:plain

構成3で、一方のPCを無線接続にしたもの。

5GHzの時はNetMiで10Mbps, ファイル送受信で100Mbps。
2.4GHzの時はNetMiで18Mbps, ファイル送受信で80Mbps。


まとめ

誰か正しい測定方法を教えてください。

ジャンボフレームとかLANケーブルとかディスクの読み出し速度とか、
色々ボトルネックになりそうなところはありますが、何が原因かはまだ分かっていません。

今日から社会人2年目。そろそろ自分のコアスキルに自信を持ちたいところです。

有線LANと無線LANの優先度を切り替える

会社でノートPCを使っていると、無線LANと有線LANを使い分けたいことがあります。
イントラネットには無線でしかアクセスできないけど、
運用LANには有線でしかアクセスできないみたいなパターンですね。
こんな時、PCのルーティングテーブルが適切に設定されていないと、
無線で通信するべきネットワークに有線でつないだり、その逆の事態が生じたりします。
これを解消するのがrouteコマンドです。
クライアントOSはWindows 7、ネットワーク構成は以下の前提で説明していきます。

f:id:moga_shi:20130330235647p:plain

まず、route printコマンドでPCの現在のルーティングテーブルを確認します。

C:\Users\u123456>route print
===========================================================================
インターフェイス一覧
 11...f0 de f1 ** ** ** ......Intel(R) 82579LM Gigabit Network Connection
 12...10 0b a9 ** ** ** ......Intel(R) Centrino(R) Advanced-N 6205
  1...........................Software Loopback Interface 1
===========================================================================

IPv4 ルート テーブル
===========================================================================
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0        10.192.1.254      10.192.1.100     20
       10.192.1.0    255.255.255.0            リンク上      10.192.1.100    266
     10.192.1.100  255.255.255.255            リンク上      10.192.1.100    266
     10.192.1.255  255.255.255.255            リンク上      10.192.1.100    266
        127.0.0.0        255.0.0.0            リンク上         127.0.0.1    306
        127.0.0.1  255.255.255.255            リンク上         127.0.0.1    306
  127.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
        224.0.0.0        240.0.0.0            リンク上         127.0.0.1    306
        224.0.0.0        240.0.0.0            リンク上      10.192.1.100    266
  255.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
  255.255.255.255  255.255.255.255            リンク上      10.192.1.100    266
===========================================================================
固定ルート:
  なし


この構成だと、有線LANでしか接続できないネットワークや
社内のプロキシでブラックリストに登録されているサイトにアクセスできません。
ここに有線LANを接続します。

f:id:moga_shi:20130331005228p:plain

C:\Users\u123456>route print
===========================================================================
インターフェイス一覧
 11...f0 de f1 ** ** ** ......Intel(R) 82579LM Gigabit Network Connection
 12...10 0b a9 ** ** ** ......Intel(R) Centrino(R) Advanced-N 6205
  1...........................Software Loopback Interface 1
===========================================================================

IPv4 ルート テーブル
===========================================================================
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0        10.192.1.254      10.192.1.100     20
       10.192.1.0    255.255.255.0            リンク上      10.192.1.100    266
     10.192.1.100  255.255.255.255            リンク上      10.192.1.100    266
     10.192.1.255  255.255.255.255            リンク上      10.192.1.100    266
        127.0.0.0        255.0.0.0            リンク上         127.0.0.1    306
        127.0.0.1  255.255.255.255            リンク上         127.0.0.1    306
  127.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
      192.168.1.0    255.255.255.0            リンク上     192.168.1.100    261
    192.168.1.100  255.255.255.255            リンク上     192.168.1.100    261
    192.168.1.255  255.255.255.255            リンク上     192.168.1.100    261
        224.0.0.0        240.0.0.0            リンク上         127.0.0.1    306
        224.0.0.0        240.0.0.0            リンク上     192.168.1.100    261
        224.0.0.0        240.0.0.0            リンク上      10.192.1.100    266
  255.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
  255.255.255.255  255.255.255.255            リンク上     192.168.1.100    261
  255.255.255.255  255.255.255.255            リンク上      10.192.1.100    266
===========================================================================
固定ルート:
  なし


有線LANに接続しているので有線LAN向けの通信ができそうですが、デフォゲが無線LANルータの方を向いているので、
宛先ノードが自分と同じネットワークに存在しない場合、
パケットが無線LANルータの方に行ってしまいます。
かといって、すべてのパケットを有線LAN経由にすると、今度は無線LANでしか
通信できないネットワークと通信できなくなってしまいます。


案1

要件を以下のように定めます。

f:id:moga_shi:20130331005230p:plain

  • 無線LAN経由でしか通信できないネットワーク宛のパケットは必ず無線LANルータ経由にする。
  • 宛先が自分と同じネットワークに存在しないパケットは、
    • PCにLANケーブルが接続されている時はそっち経由にする。
    • 接続されていない時は無線LANルータ経由にする。


実行するコマンドは以下の通りです。

C:\Users\u123456>route -p add 0.0.0.0 mask 0.0.0.0 192.168.1.254 metric 10

デフォルトルートを有線LANルータ宛に設定。メトリックが無線LANルータよりも小さくなるように。
-pは再起動後もルートを維持するオプション。


C:\Users\u123456>route -p add 10.240.0.0 mask 255.255.0.0 10.192.1.254

イントラネット宛の通信は必ず無線LANルータ経由にする。
メトリックは省略すると1になる。


なお、不幸にも有線LANと無線LANでネットワークアドレスが重複してしまう場合は、
route printコマンドの一番上に出てくるインタフェースを番号で指定します。(今回は不要です)

C:\Users\u123456>route -p add 10.240.0.0 mask 255.255.0.0 10.192.1.254 if 12


これで完成となりますが、実はこの設定だと以下の問題があって実用性に乏しいです。

  • DNSの名前解決が有線LAN経由になるため、社内システムのホスト名を解決できない。

案2

上記の問題を解決するために、仕様を以下のように変更します。

f:id:moga_shi:20130331005231p:plain

  • 基本は無線LANルータ経由で通信する。
  • 有線LANで接続したいネットワークだけroute addで追加する。

コマンドは次のとおりです。

C:\Users\u123456>route delete 0.0.0.0
C:\Users\u123456>route delete 10.240.0.0

先程登録したルートを削除します。


C:\Users\u123456>route -p add 0.0.0.0 mask 0.0.0.0 10.192.1.254 metric 20

デフォルトゲートウェイ無線LANルータにします。


C:\Users\u123456>route -p add 173.194.38.96 mask 255.255.255.240 192.168.1.254

有線LANで接続したいルートを個別に登録します。
ブラウザで閲覧する時はプロキシの例外設定を忘れないようにしてください。


ルーティングテーブルの最終形は次のようになります。

C:\Users\u123456>route print
===========================================================================
インターフェイス一覧
 11...f0 de f1 ** ** ** ......Intel(R) 82579LM Gigabit Network Connection
 12...10 0b a9 ** ** ** ......Intel(R) Centrino(R) Advanced-N 6205
  1...........................Software Loopback Interface 1
===========================================================================

IPv4 ルート テーブル
===========================================================================
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0        10.192.1.254      10.192.1.100     20
       10.192.1.0    255.255.255.0            リンク上      10.192.1.100    266
     10.192.1.100  255.255.255.255            リンク上      10.192.1.100    266
     10.192.1.255  255.255.255.255            リンク上      10.192.1.100    266
        127.0.0.0        255.0.0.0            リンク上         127.0.0.1    306
        127.0.0.1  255.255.255.255            リンク上         127.0.0.1    306
  127.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
    173.194.38.96  255.255.255.240       192.168.1.254     192.168.1.100      6
      192.168.1.0    255.255.255.0            リンク上     192.168.1.100    261
    192.168.1.100  255.255.255.255            リンク上     192.168.1.100    261
    192.168.1.255  255.255.255.255            リンク上     192.168.1.100    261
        224.0.0.0        240.0.0.0            リンク上         127.0.0.1    306
        224.0.0.0        240.0.0.0            リンク上     192.168.1.100    261
        224.0.0.0        240.0.0.0            リンク上      10.192.1.100    266
  255.255.255.255  255.255.255.255            リンク上         127.0.0.1    306
  255.255.255.255  255.255.255.255            リンク上     192.168.1.100    261
  255.255.255.255  255.255.255.255            リンク上      10.192.1.100    266
===========================================================================
固定ルート:
  ネットワーク アドレス          ネットマスク  ゲートウェイ アドレス  メトリック

          0.0.0.0          0.0.0.0   10.168.195.254      20
    173.194.38.96  255.255.255.240    192.168.1.254       1
===========================================================================

その他

今回はWindowsのrouteコマンドをご紹介しましたが、Linuxにもrouteコマンドがあります。
書き方は異なりますが、基本的な動作は同じだと思われるので、詳しいことは以下のサイトなどをご覧ください。
Linuxコマンド集 - 【 route 】 ルーティング・テーブルを表示・設定する:ITpro


課題

案1で、名前解決だけを無線LAN経由にできれば問題は解決できたのではないかと思いますが、その方法が分かりませんでした。
分かり次第追記しますが、ご存知の方がいればご教示ください。