SummerWind

Web, Photography, Space Development

Node.jsとSTUN

リアルタイム処理について色々考えていたら、端末同士が直接データをやり取りすることに興味を持ち始めたので、最近はUDPやらNAT越え関連で遊んでいます。

UDPを使って端末同士が直接通信する時にほぼ必ず問題となるのがNAT越えです。端末がNAT配下にいる場合、NATのアドレスとポートを通信相手に伝えられなければ直接通信をおこなうことができません。P2PやVoIPによる通信も同じようにNATが問題になるわけですが、それをなんとかしようと出てきたのがNAT traversalです。

NAT traversalにも色々なテクニックがあるようですが、代表的なものとしてUDPホールパンチングというものがあります。これはグローバルIPアドレスを持つサーバーに対してUDPパケットを送信することで、NATからアドレスとポート割り当ててもらい、そのアドレスとポートのペアをサーバーからのUDPのレスポンスとして教えてもらう、というものです。その仕組みを実装したのが、RFC5389として定義されているSession Traversal Utilities for NAT (STUN)です。最近はWebRTCのNAT越えの一部にも使われているみたいですね。

さて、前置きが長くなりましたが、今回はNode.jsでUDPホールパンチングをしてみようということで、STUNプロトコルを扱うためのモジュール「node-stun」を実装しました。以下のようにnpmコマンドからインストールすることができます。

$ npm install stun

サンプルコードは以下の通り。GoogleのSTUNサーバーに接続してリクエストすれば、NATの公開アドレスとポートが取得できると思います。

var stun  = require('stun');

// STUNサーバー (by Google)
var port = 19302;
var host = 'stun.l.google.com';

// クライアントを生成
var client = stun.connect(port, host);

// レスポンスハンドラ
client.on('response', function(packet){
    client.close();
    var nat = packet.attrs[stun.attribute.MAPPED_ADDRESS];
    console.log('Port:', nat.port, ' / Address:', nat.address);
});

// STUNリクエストを送信
client.request();

STUNの仕様には認証や再送処理なども含まれるのですが、このモジュールには実装していません。サーバーの実装も少し考えているので、もうちょっと勉強したら実装するかもしれません。

そうそう、このモジュールを使ってUDPホールパンチングしたアドレスを使って、UDPポートでクライアント間通信を試してみましたが、我が家のプロバイダ環境では実際にはうまく通信できませんでした。STUNは限られたNAT環境でしか有効ではないので、もう少しNATの種類とその検出方法について勉強しようと思います。

Moto Ishizawa

Moto Ishizawa
ソフトウェアエンジニア。ロケットの打上げを見学するために、たびたびフロリダや種子島にでかけるなど、宇宙開発分野のファンでもある。