インバウンドトラフィック用にプロバイダを追加契約した
現状のネットワークこんな感じだけど以下の問題があった。
- メインWANルータ(WZR-1750DHP)にヘアピンNAT機能がなく、iPhoneテザリング等を使わないとグローバルからの見え方が分からない。
- WAN回線が固定IPではないため、PPPoEのセッションが切断された際にDDNSへの再登録が必要になり復旧時間が長くなる。また、VPNの接続形態に制約が生じる。
- 検証目的でネットワーク構成を変更した際、設定ミスによりサーバへのアクセスができなくなる可能性がある。
そこで固定IP1で料金の安いグリーンネットというISPと契約した。
今はBフレでもPPPoE 2セッション貼れるので最高便利。
変更後の構成は以下の通り。
インターネットアクセス用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を作った
きんモザが終わる。
毎週楽しみにしていたきんモザが。
寂しい。
アリスと会えなくなることが、寂しい。
クエリストリング無しでアクセスするとランダムで 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のスペックは前回と同じですが、一応再掲。
自作PCのアイコンがノートPCになってるのは気にしないでくださいw
構成1
まずは有線接続から。右側の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
右側の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
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
$ 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くらいでした。
比較
最後に構成別の測定結果(中央値)をグラフにして掲載します。
全体的に、無線を経由すると速度が低下するようですね。
バッファローの測定結果では11acで最低でも500Mbpsは出ていたらしいですが・・・。
ただ、自分の測定方法がいい加減なので、メモリやバスなどの転送速度がボトルネックになっていたり、
ルータの設定方法が甘かったりして、やり方によってはもっと好成績を出せるのかもしれません。
まだひよっこSEなもので、ここをこうしたらいいよ!っていうアドバイスがあればお願いします。
興味を惹くものがあれば、ぜひ人柱としてIYH!!!させていただきますw
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
PC同士を有線LANでつないだだけの構成。
今まで100BASE-TXのルータを使っていたので、どれくらい早くなるかなぁと思って試してみました。
NetMiでは393Mbps、ファイルの送受信はおよそ400Mbpsでした。
普通に800Mbps以上は出ると思っていたので、あれ?という感想です。
構成2
一方のPCを有線、もう一方のPCを無線でつないだ構成。
5GHzの時はNetMiで18Mbps, ファイル送受信で140Mbps。
2.4GHzの時はNetMiで22Mbps, ファイル送受信で95Mbps。
この辺りから計測結果の信憑性がまるでダメになってきます。
構成3
一方のルータを親機、もう一方のルータを中継機として、PC-ルータ間は有線でつなぐ構成。
NetMiでは31Mbps、ファイルの送受信はおよそ520Mbpsでした。
……何なんでしょう、この差は。
構成4
構成3で、一方のPCを無線接続にしたもの。
5GHzの時はNetMiで10Mbps, ファイル送受信で100Mbps。
2.4GHzの時はNetMiで18Mbps, ファイル送受信で80Mbps。
まとめ
誰か正しい測定方法を教えてください。
ジャンボフレームとかLANケーブルとかディスクの読み出し速度とか、
色々ボトルネックになりそうなところはありますが、何が原因かはまだ分かっていません。
今日から社会人2年目。そろそろ自分のコアスキルに自信を持ちたいところです。
有線LANと無線LANの優先度を切り替える
会社でノートPCを使っていると、無線LANと有線LANを使い分けたいことがあります。
イントラネットには無線でしかアクセスできないけど、
運用LANには有線でしかアクセスできないみたいなパターンですね。
こんな時、PCのルーティングテーブルが適切に設定されていないと、
無線で通信するべきネットワークに有線でつないだり、その逆の事態が生じたりします。
これを解消するのがrouteコマンドです。
クライアントOSはWindows 7、ネットワーク構成は以下の前提で説明していきます。
まず、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を接続します。
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
要件を以下のように定めます。
- 無線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
上記の問題を解決するために、仕様を以下のように変更します。
- 基本は無線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経由にできれば問題は解決できたのではないかと思いますが、その方法が分かりませんでした。
分かり次第追記しますが、ご存知の方がいればご教示ください。
Node.js+Socket.IO+Express+MongoDB で実装するチャットサンプル [deprecated]
(※注:2012年当時の記事のため内容はかなり古いです。いつか書き直したい・・・)
2012年もスタートして早5日。
せっかくの心機一転のチャンスだし、何か新しい技術を覚えてみたいと思い、WebSocket の勉強をしてみることにしました。
既にご存知の方も多いと思いますが、WebSocket とはブラウザ上で Web サーバとの双方向通信を可能にする規格のことで、
HTML5 とともに、Web アプリケーションがこれから全盛期を迎えるだろう時代にあっては必須とも言えるものです。
ブラウザ上で双方向通信を行うための規格としては、これまでにも Ajax や Comet といったものがありましたが、
サーバからのプッシュ通信ができないことや負荷が高いといった問題があり、
それらを簡潔な実装で解消できる技術が求められていたようです。
※Ajax, Comet, WebSocket の具体的な相違点については、こちらのスライドで詳しく説明されています。
以上のように、WebSocket はとても将来性のある技術ですが、現時点では API やプロトコルの仕様が固まっていないことや、一部の古いブラウザが対応していないということもあり、実際にアプリケーションを作る際にはその点を考慮しなければなりません。
今回は、そうしたブラウザ間の差異を吸収し、一度の実装で全ブラウザ対応を可能にしてくれる「Socket.IO」というライブラリを使用することにしました。
また、アプリケーションを動かすサーバとして、「Node.js」という、JavaScriptで動かせるサーバを使うことにしました。
作るものはリアルタイムチャットです。
ここから実装の話に入っていきますが、便宜上、OS は CentOS を使うことにします。
それから、自分自身がroot権限で作業を行ったということもあり、あまり良くはないと思いますが、念のためroot権限で行っていきます。
まず、Node.js のインストール。
# cd /usr/local/src # wget <a href="http://nodejs.org/dist/v0.6.5/node-v0.6.5.tar.gz" style="color:#ccf;">http://nodejs.org/dist/v0.6.5/node-v0.6.5.tar.gz</a> # tar xzvf node-v0.6.5.tar.gz # cd node-v0.6.5 # ./configure # make # make install
これで /usr/local/bin/node が作成されます。
次に、Socket.IO のインストール。
# npm install socket.io socket.io@0.8.7 ./node_modules/socket.io
npmというのは Node Package Manager、つまり Node.js 関係のパッケージ管理システムで、これを使って Socket.IO をインストールしています。
今回は Socket.IO の v0.8.7 を使いますが、Socket.IO は 0.7系から仕様が大きく変わっており、同じコードでもバージョンによって動かなくなることがあります。
(自分はこれにハマってドキュメントの大切さを思い知りました…)
この記事の内容も数ヶ月後にはもう動かなくなっているかもしれないので、ぜひ本家のドキュメントもご覧ください。
https://github.com/LearnBoost/socket.io
続いて、Express と EJS をインストールします。
Express は Node.js アプリを作るのに必要なファイルを自動生成してくれるフレームワークで、EJS は Node.js アプリの表示機能の実装をちょっと楽にしてくれるテンプレートエンジンです。
# npm install -g express ejs # express sandbox # cd sandbox # sandbox 内で必要なモジュールが使えるようにします。 # 元のモジュールをグローバルオプションでインストールしているので npm link express ejs でいけるはずなのですが # なぜがうまくいかなかったので、リソースの無駄遣いと知りつつ二回インストールしています。 # npm install express ejs
express sandbox を実行すると、sandbox というディレクトリが作成されて、その中に Node.js アプリに必要なファイル郡が作られます。
具体的には以下の通り。
sandbox │ ├ node_modules │ │ │ ├ express │ │ └... │ └ jade │ └... // 今回は使わない ├ views │ ├ layout.jade // 今回は使わない │ └ index.jade // 今回は使わない │ ├ routes │ └ index.js // 今回は使わない │ ├ public │ ├ stylesheets │ │ └ style.css │ ├ javascripts │ └ images // 今回は使わない │ ├ package.json │ └ app.js // サーバーサイドで動くJavaScriptファイル
この状態で
# node app.js
を実行して、http://203.0.113.0:3000(サーバのIPアドレスを203.0.113.0と仮定しています)
にアクセスすると、「Welcome to Express」という画面が表示されるかと思います。
以下ではこの app.js やテンプレートファイルを書き換えてチャットアプリを作っていきます。
まず、必要なロジックとして挙げられるのは
- クライアント端末からサーバにメッセージを送信する機能(クライアント側で実装)
- サーバが受信したメッセージを全てのクライアント端末に対して送信する機能(サーバ側で実装)
- クライアント端末が受信したメッセージを画面に表示する機能(クライアント側で実装)
の3つです。
(以下、クライアントからサーバに送信するメッセージの名前を 'msg send', サーバからクライアントに送信するメッセージの名前を 'msg push' とします。)
これに表示用のテンプレートファイルを加えた結果、ファイル構成は
sandbox │ ├ node_modules // 各種モジュール │ │ │ ├ express │ │ └... │ └ ejs │ └... ├ views │ └ index.ejs // 表示用テンプレート │ ├ public // クライアント側で使用するファイル群 │ │ │ ├ stylesheets │ │ └ style.css │ └ javascripts │ └ client.js // クライアント側のJavaScriptファイル │ ├ package.json │ └ app.js // サーバーサイドで動くJavaScriptファイル
となります。まずはクライアントの表示用ファイルから。
[index.ejs]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" href="/stylesheets/style.css" /> </head> <body> <script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript">google.load("jquery", "1.4.4");</script> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript"> var port = <%= port %>; </script> <script type="text/javascript" src="/javascripts/client.js"></script> <h1><%= title %></h1> <form id="form1"> <input type="text" id="message" /> <input type="submit" value="送信" /> </form> <ul></ul> </body> </html>
必要なファイルの読み込みと、簡単なタグだけです。<%= port %> と <%= title %> のところには、後で [app.js] で定義する値が入ります。
続いてクライアントのJavaScript。
[client.js]
$(function() { var socket = new io.connect('http://203.0.113.0:'+port); // ▼ チャットフォーム ▼ $('#form1').submit(function() { msg = $('#message').val(); msg = sanitize(msg); socket.emit('msg send', msg); $('#message').val(''); return false; }); // ▲ チャットフォーム ▲ // メッセージ受信 socket.on('msg push', function(msg, date) { $('ul').prepend('<li>'+msg+' ('+date+')</li>'); }); }); function sanitize(str) { str = str.replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); return str; }
サーバ(203.0.113.0:3000)に接続した後、フォームのsubmitボタンを押すと
サーバに対して 'msg send' という名前のメッセージを、変数 msg とともに送信します。
メッセージはサーバ側で処理された後、'msg push' という名前のメッセージを、
変数 msg, date とともに返すものとしています。
最後にサーバ側のJavaScript。
[app.js]
var express = require('express') , ejs = require('ejs') , io = require('socket.io') var app = module.exports = express.createServer(); var title = 'チャットテスト'; var port = 3000; app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.set('view options', { layout: false }); app.use(express.static(__dirname + '/public')); }); app.get('/views', function(req, res) { res.render('index', { locals: { port:port, title:title } }); }); app.listen(port); console.log("Express server listening on port %d", app.address().port); var socket = io.listen(app); // ▼ 接続時実行 ▼ socket.sockets.on('connection', function(client) { // ▼ クライアントからサーバへメッセージ送信 ▼ client.on('msg send', function(msg) { date = getDateAndTime(); if (msg == '') return; client.emit('msg push', msg, date); client.broadcast.emit('msg push', msg, date); }); // ▲ クライアントからサーバへメッセージ送信 ▲ }); // ▲ 接続時実行 ▲ // 現在の日時を YYYY/MM/DD hh:mm:dd 形式で返す関数 function getDateAndTime() { dd = new Date(); year = (dd.getYear() < 2000 ? dd.getYear()+1900 : dd.getYear() ); month = (dd.getMonth() < 9 ? "0" + (dd.getMonth()+1) : dd.getMonth()+1 ); day = (dd.getDate() < 10 ? "0" + dd.getDate() : dd.getDate() ); hour = (dd.getHours() < 10 ? "0" + dd.getHours() : dd.getHours() ); minute = (dd.getMinutes() < 10 ? "0" + dd.getMinutes() : dd.getMinutes() ); second = (dd.getSeconds() < 10 ? "0" + dd.getSeconds() : dd.getSeconds() ); return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second; }
ここで、
# node app.js
を実行してから http://203.0.113.0:3000/views にアクセスすると、socket.sockets.on の中身が実行され、'msg send' というメッセージの受信待ち状態になります。(client.on の部分)
メッセージを受信すると、
client.emit('msg push', msg, date);
でそのメッセージの送信者に対して 'msg push' という名前のメッセージを返信するほか、
client.broadcast.emit('msg push', msg, date);
で接続中の残り全てのクライアントに対して同じメッセージを送信します。
ブラウザでメッセージを送信すると、他のブラウザにも瞬時に反映されることが確認できます。
以上で最低限の機能の実装は終わりです。
この状態だと、入力したメッセージはその時チャットを開いていたブラウザに表示されるだけでそのまま消えてしまうので、会話が成立するためにはお互いが同じタイミングで居合わせる必要があります。
このままでは実用性に乏しいため、MongoDB というデータベースを使ってログの保存・読み込み機能を実装します。
MongoDB の概要、Node.js との相性の良さについてはこちらのスライドが詳しいです。
www.slideshare.net
インストール作業、やはりroot権限でやっています。
まずは yum のリポジトリを追加。こちらのブログを参考にしました。
http://memo.yomukaku.net/entries/tiSGwUw
# vi /etc/yum.repos.d/10gen.repo [10gen] name=10gen Repository baseurl=<a href="http://downloads-distro.mongodb.org/repo/redhat/os/x86_64" style="color:#ccf;">http://downloads-distro.mongodb.org/repo/redhat/os/x86_64</a> gpgcheck=0 enabled=0 # yum search mongo --enablerepo=10gen =========================== Matched: mongo =========================== libuninum.x86_64 : Library for converting unicode strings to numbers mongo-10gen.x86_64 : mongo client shell and tools mongo-10gen-server.x86_64 : mongo server, sharding server, and support : scripts mongo18-10gen.x86_64 : mongo client shell and tools mongo18-10gen-server.x86_64 : mongo server, sharding server, and : support scripts # yum install mongo-10gen-server.x86_64 --enablerepo=10gen # mongod --version db version v2.0.2, pdfile version 4.5 # npm install mongodb // sandbox ディレクトリ直下で実行
/usr/bin に MongoDB とその関連モジュールがインストールされました。
起動スクリプトは /etc/rc.d/init.d/mongod に、設定ファイルは /etc/mongod.conf にあります。
せっかくなので MongoDB がどんなものなのか使ってみましょう。
# mongo MongoDB shell version: 2.0.2 connecting to: test > show dbs admin (empty) local (empty) > db.things.insert({"name": "rice ball", "price": 105}) > db.things.insert({"name": "cup noodle", "price": 188}) > db.things.insert({"name": "water", "price": 98}) > db.things.find() { "_id" : ObjectId("4f044fb116df0fb8470aae4d"), "name" : "rice ball", "price" : 105 } { "_id" : ObjectId("4f04504916df0fb8470aae4e"), "name" : "cup noodle", "price" : 188 } { "_id" : ObjectId("4f04505e16df0fb8470aae4f"), "name" : "water", "price" : 98 }</div>
test というデータベース配下の things というコレクションに 3件のドキュメントを追加しています。
コレクションはリレーショナルデータベースではテーブルに相当するもので、ドキュメントは行に相当するものです。
データ構造を決めずにいきなりデータを追加できるという手軽さが面白いです。
ドキュメントを探す時は
> db.things.find({"name": "rice ball"}) { "_id" : ObjectId("4f044fb116df0fb8470aae4d"), "name" : "rice ball", "price" : 105 }
のようにできます。
> db.things.update({name: "rice ball"}, {$set: {price: 210}}) > db.things.find({"name": "rice ball"}) { "_id" : ObjectId("4f044fb116df0fb8470aae4d"), "name" : "rice ball", "price" : 210 }
値上がりさせてみました。
それでは実際に MongoDB を使って、先程作った [client.js], [app.js] を書き換えていきます。( index.ejs はそのままです)
[client.js]
$(function() { var socket = new io.connect('http://203.0.113.0:'+port); // ▼ チャットフォーム ▼ $('#form1').submit(function() { msg = $('#message').val(); msg = sanitize(msg); socket.emit('msg send', msg); $('#message').val(''); return false; }); // ▲ チャットフォーム ▲ // メッセージ受信 socket.on('msg push', function(msg, date) { $('ul').prepend('<li>'+msg+' ('+date+')</li>'); }); // ログの読み込み socket.on('msg load', function(msg, date) { $('ul').append('<li>'+msg+' ('+date+')</li>'); }); }); function sanitize(str) { str = str.replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); return str; }
チャットにアクセスした時に、サーバから 'msg load' という名前のメッセージを
クライアントが受け取り、表示する機能を加えています。
[app.js]
var express = require('express') , ejs = require('ejs') , io = require('socket.io') , mongo = require('mongodb') , db = new mongo.Db('test', new mongo.Server('203.0.113.0', 27017, {}), {}); var app = module.exports = express.createServer(); var title = 'チャットテスト'; var port = 3000; app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.set('view options', { layout: false }); app.use(express.static(__dirname + '/public')); }); app.get('/views', function(req, res) { res.render('index', { locals: { port:port, title:title } }); }); app.listen(port); console.log("Express server listening on port %d", app.address().port); var socket = io.listen(app); // ▼ 接続時実行 ▼ socket.sockets.on('connection', function(client) { // ▼ 初回アクセス時 最新20件のログを取得して送信 ▼ db.open(function(){ db.createCollection("comments", function(err, collection) { cursor = collection.find({}, {"limit":20, "sort":[['unixtime','desc']] }); cursor.each(function(err, doc) { if (doc != null) client.emit('msg load', doc.message, doc.date); }); }); }); // ▲ 初回アクセス時 最新20件のログを取得して送信 ▲ // ▼ クライアントからサーバへメッセージ送信 ▼ client.on('msg send', function(msg) { date = getDateAndTime(); unixtime = new Date()/1000; if (msg == '') return; // comments コレクションに保存 db.open(function() { db.collection("comments", function(err, collection) { collection.insert({"message":msg, "date":date, "unixtime":unixtime}); console.log('*** DB insert ***'); }); }); client.emit('msg push', msg, date); client.broadcast.emit('msg push', msg, date); }); // ▲ クライアントからサーバへメッセージ送信 ▲ }); // ▲ 接続時実行 ▲ // 現在の日時を YYYY/MM/DD hh:mm:dd 形式で返す関数 function getDateAndTime() { dd = new Date(); year = (dd.getYear() < 2000 ? dd.getYear()+1900 : dd.getYear() ); month = (dd.getMonth() < 9 ? "0" + (dd.getMonth()+1) : dd.getMonth()+1 ); day = (dd.getDate() < 10 ? "0" + dd.getDate() : dd.getDate() ); hour = (dd.getHours() < 10 ? "0" + dd.getHours() : dd.getHours() ); minute = (dd.getMinutes() < 10 ? "0" + dd.getMinutes() : dd.getMinutes() ); second = (dd.getSeconds() < 10 ? "0" + dd.getSeconds() : dd.getSeconds() ); return year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second; }
これでログの保存・読み込み機能ができました。
初めての記事にしてはだいぶ長くなってしまいましたが、ひとまずこれで終了します。
今回は Node.js から MongoDB にアクセスするドライバとして、
# npm install mongodb
でインストールされる node-mongodb-native を使用しましたが、この後色々チャットを改造していてコールバック地獄を味わったというのもあり、mongoose にも興味を持ちました。
また時間のあるときに試したいと思います。