スキップしてメイン コンテンツに移動

投稿

2014の投稿を表示しています

[メモ] DartをCentOSにインストールする

DartをCentOS release 6.4 (Final)なんかで使おうとすると、「GLIBC_2.15' not found」と表示されて上手く動作しないことがある。
なので、自前でビルドする必要がある。 例えば、version1.5のdartを利用した場合は、以下のような感じで環境を作る事ができる。
[How to build/install dart sdk in centos 2014/07/20]
  [create env]    sudo yum -y install subversion    sudo yum -y install make    sudo yum -y install gcc-c++    sudo yum -y install java-1.6.0-openjdk-devel    gclient config http://dart.googlecode.com/svn/branches/1.5/deps/all.deps    gclient sync    gclient runhooks   [build]    cd dart    tools/build.py --mode=release --arch=x64 create_sdk   [ref]    https://code.google.com/p/dart/wiki/BuildingOnCentOS
上手くビルドできたら、 dart/out/ReleaseX64/dart-sdk/bin は以下にパスを通してあげればOK。

パッケージをアップデートしたい時
   pub get    pub upgrade
実行したい時 dart xxx.dart

といった感じで、コマンドから操作できます。


P2P探訪 WebRTC用、SDP交換Peerを作ってみた。〜WebRTCで転送する〜

続きです。
WebRTCのPeerに、SDPデータを転送する機能を追加してみました。


WebRTCを用いてP2P通信を確立するには、SDPを交換する必要があるのでした。SDPの交換はWebRTCフレームワークで提供されていないので、自作する必要があったのでした。

今回は、WebRTCのPeerにSDPデータを交換する機能を実現してみました。
本機能によって、一度ネットワークに参加する事が出来たならば、サーバーを経由せずに、P2Pネットワークを成長させる事ができるようになります。


[仕組み] WebSocketの場合とほとんど同じです。通信方法がWebSocketから、WebRTCのデータコネクションに変わるだけです。
仲介役のPeerが指定されたアドレス(uuid)のPeetへメッセージを送信する機能を実現するだけで良いでしょう。

例えば、以下のような感じで書けます。

function MessageTransferBase(target) { this.mParent = target; // 他のPeerからメッセージを受け取った MessageTransferBase.prototype.onReceiveMessage = function(caller,message) { }; //仲介者(transfer)を経由して、メッセージを送信してもらう。 MessageTransferBase.prototype._sendUnicastMessage = function(transfer, to, from, content) { console.log("======sendUnicastMessage :"); var mes = {}; mes.messageType = "unicast"; mes.to = to; mes.from = from; mes.content = content; this.mParent.getPeerList().get(transfer).caller.sendMessage(JSON.stringify(mes)); } //仲介の依頼ならば、仲介してあげる。自分へのメッセージならば、…

P2P探訪 WebRTC用、SDP交換サーバーを作ってみた。〜WebSocketで転送する〜

続きです。
WebSocketを用いて、SDPデータを転送する機能を実現しました。


[コネクションを維持して任意のタイミングで通信] 通常、ブラウザーでサイトにアクセスする場合、ブラウザーは必要なデータをダウンロードすると、コネクションを切ります。

コネクションが切れてしまうと、サーバーからクライアントへメッセージを送信する事ができません。
なので、以前は、ホームページの表示完了後、少し時間がたってからサーバーから何かしらのPush通信を受けると行った事が出来ませんでした。

しかし、WebSocketが誕生しこの状況はいっぺんします。
WebSocketを使った場合、サーバーとクライアントのコネクションはクローズされず、維持し続けます。これによって、サーバーから、クライアントへメッセージを送信する事ができるようになりました。

WebSocketを使えば、任意のタイミングで、サーバーとブラウザーが通信できます。
この機能を利用すれば、容易にSDP交換サーバーの実現することができるでしょう。

[SDP交換サーバーの役目]
SDP交換サーバーの役目は、(a)Peerの存在を他のPeerへ伝える事。そして、(b)SDP情報を、Peer同士で交換できるようにする事です。

今回作成した、SDP交換サーバーでは、以下の機能を実装しました。
[1]  Peer全体にメッセージを送信する。
[2] 指定したUUIDのPeerへメッセージを送信する。


あたらに加わったPeerが、「[1]の機能」を用いて、p2pネットワーク全体にUUIDを送信すれば、「(a)の目的」を果たす事ができます。
また、P2P接続を確立したい場合、「[2]の機能」を用いて、他のPeerへ自身のSDPを送る事ができるでしょう。


[実装] nodejsを使って実現しました。
https://github.com/kyorohiro/HelloWebRTC/blob/master/signalServer/signalserver.njs

// httpサーバーを立ち上げる this.mHttpServer = HTTP.createServer(function (req, res) { ... } // websocketサーバーを立ち上げる this.…

P2P探訪 WebRTC用、SDP交換サーバーを作ってみた。〜 UUIDでPeerを識別する 〜

WebRTCを用いて、P2P通信をする場合には、SDPというデータを交換する必要がある事を説明しました。
ただし、このSDPを交換する部分はWebRTCでは提供されていません。自作する必要があるでしょう。※有り物を拝借するでも良いです。

試しに、WebSocketを使ってSignalServer(SDP交換機)を作成してみました。紹介します。


[UUIDでPeerを識別する] 多数のPeerの交換器としてサーバーを動作させたい場合、各Peerを識別する方法が必要です。
特にP2Pシステムでは、統制を取らずに一意の識別子の作成をする事が望ましいです。
※今は交換サーバーが一つを想定しているので、本来考慮する事ないかもしれません。

今回は乱数を使用して実現しました。128bitの値を乱数で生成します。0〜2**128の値衝突する確率は、1/2**64ととても少ないのです。
※ アレと思った方は、「誕生日攻撃」ググると良い
// ref http://note19.com/2007/05/27/javascript-guid-generator/ function s4() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } function createUUID() { return s4()+s4()+"-"+s4()+"-"+s4()+"-"+s4()+"-"+s4()+s4()+s4(); } ※rfc4122では、最初の6bitは予約されています。


[次回] 続きを書く..。その続きは、Peerに交換機の機能を付けたサンプルを紹介する予定。

※ 成果物は以下
https://github.com/kyorohiro/HelloWebRTC/tree/master/signalServer





P2P探訪 StunでNat越え その7 WebRTCでShakehand

前回、Stunの実例として、WebRTCを利用してSDP(自身のアドレスとポート)を取得しました。せっかくなので、WebRTCを使って、Peer同士でメッセージのやり取してみましょう。


WebRTCを用いて、お互いのPeerが接続してメッセージを送る方法は簡単です。
1. 自分のSDPと相手のSDPを取得する。
2. 取得したSDPを設定する。
3. メッセージを送信する。※今回はテキスト

とするだけです。

[解説] 以前説明した通り、Peerどうしが接続するためには、接続相手のアドレスを知る必要があります。このアドレスを知りたい場合には、Stunサーバーを利用するのでした。

また、接続される側も、接続してくるPeerのアドレスがわかっていれば、「UDPパンチ」などを用いて、接続できる可能性をあげる事ができるのは、ご存知のことでしょう。以前解説したとおりです。

WebRTCも同様の手法を取っています。「接続される側」、「接続する側」のアドレスを前もって、WebRTCの知らせる事で、Peer同士が接続できるようになります。

※注意点
WebRTCでは、「接続される側」、「接続する側」のアドレスを、Peerに知らせる方法は提供されていません。
独自に実装する必要があります。WebSocketを利用する方法流行っているみたいです!!



[接続の処理の流れ] 実際の処理の流れを見てみましょう。接続が完了すれば、メッセージを送信できるようになります。

○自分のSDPを取得/設定
#接続を要求する側
O-1. RTCPeerConnectionを生成する。
O-2. RTCPeerConnection#createOffer()をコールする。
O-3. RTCPeerConnection#setLocalDescription()をコールして設定する
#接続を受け入れる側
A-1. RTCPeerConnectionを生成する
A-2. RTCPeerConnection#createAnswer()をコールする
A-3. RTCPeerConnection#setLocalDescription()をコールして設定する ※A-2の操作は先に接続要求してきている、SDPを設定しておく必要があります
○相手のSDPを設定する #接続を要求する側/#接続を受け入れる側
A/O-1.…

P2P探訪 StunでNat越え その6 WebRTCでSDPを取得

P2Pの起爆剤になりそうなものとして、もっとも有力なのは、WebRTCではないでしょうか。
WebRTCを用いれば、ブラウザーで、P2Pアプリの機能を提供することができます。

もちろん、WebRTCではSTUNサーバーが利用されています。
さっそく、STUNを介して、自身のアドレスを取得してみましょう。


1. RTCPeerConnection を生成する。
2. RTCPeerConnection#createOffer()をコールする

でOKです。

以下のような値を取得できます。SDPと呼ばれています。

... ... m=audio 55491 RTP/SAVPF 111 103 104 0 8 106 105 13 126 c=IN IP4 111.100.57.xxx a=rtcp:55491 IN IP4 111.100.57.214 a=candidate:38022971xx 1 udp 21139371xx 192.168.0.3 55491 typ host generation 0 a=candidate:38022971xx 2 udp 21139371xx 192.168.0.3 55491 typ host generation 0 a=candidate:12749365xx 1 udp 18455016xx 111.100.57.2xx 55491 typ srflx raddr 192.168.0.3 rport 55491 generation 0 a=candidate:12749365xx 2 udp 18455016xx 111.100.57.2xx 55491 typ srflx raddr 192.168.0.3 rport 55491 generation 0 a=candidate:28878806xx 1 tcp 15099573xx 192.168.0.3 0 typ host generation 0 a=candidate:28878806xx 2 tcp 15099573xx 192.168.0.3 0 typ host generation 0 [予告] 次回は、WebRTCを使って、何かします。たぶん、Torrentの考え方を応用したサンプルを紹介します。

○ソース
https://github.co…

P2P探訪 StunでNat越え その5

フルコーン意外は上手く通信できない NATにもさまざまな特徴がある事が理解していただけたでしょう。 具体的に、各特徴ごとに通信可能か確認してみましょう。
フルコーン <---------> フルコーン フルコーン <---------x 制限付き フルコーン <---------x シンメトリック 制限付き    x---------x 制限付き
といった感じで、フルコーンNATでなければ、受信が困難な事がわかります。
UDPパンチなら一部回避できる しかし、UDPホールパンチングというテクニックを使えばこの制限を一部回避 する事ができます。
フルコーン <---------> フルコーン フルコーン <---------> 制限付き 制限付き    <---------> 制限付き フルコーン <---------x シンメトリック
といった感じです。
前もってメッセージを送信してもらう 制限付きNATは、「送信した事がある相手からのメッセージを受け付ける」、「送信した事がない相手からのメッセージは受け付けない」を満たすように設計されています。
そこで、UDPパンチでは、これからメッセージを送ってくる相手へ、「あらかじめメッセージを送信しておく」ことで、この問題に対処しています。 相手にメッセージを送った実績があれば、制限付きNATが通信をフィルタリングすることはありません。相手からのメッセージは制限なく自分に届きます。

そもそも、UDPにおいては、通信に失敗したかどうかをNATは判断できません。 なので、 NATからは、受信したメッセージが「通信相手からのレスポンスなのか」、「新規のメッセージなのか」を判断するすべはありません。
例 ※ d1とd2は異なる端末とします。
----------------------------------------------- KyoroDatagramMock d1 = new KyoroDatagramMock(KyoroDatagramMock.NAT_TYPE_RESTRICTED_PORT);KyoroDatagramMock d2 = new KyoroDatagramMock(KyoroDatagramMock.NAT_TYPE_RESTRICTED_…

P2P探訪 STUNでNat越え その4

まずは、スーパーノード候補として最有力なNATであるか調査してみましょう。 外部から見えている、「アドレスとポート」が常に一定であり。 他からのアクセスを制限していないNATのことです。(フルコーン)

○STUNでの確認方法  どのようにして、制限があるかを確認するのでしょうか? STUNの仕組みはとても単純です。実際に通信してみて各種条件で通信ができるか試します。実際に試してみて、もしも通信できたならば、同一の条件下の端末とは通信可能といえるでしょう。

具体的には、実際に外部からメッセージを送信してもらう。そして、送信しもらったメッセージを受け取る事ができるかを確認します。
接続したUDPサーバーから、メッセージを受け取れる事接続したUDPサーバーの異なるポートから、メッセージを受け取れること接続したUDPサーバーと異なるサーバーから、メッセージを受け取る事ができる事


○Bindingリクエスト  実際にSTUNの通信内容を見ていきましょう。サーバーにレスポンスする条件(ポートとアドレス)を指定してレスポンスを返しもらいます。このレスポンスの依頼をSTUNでは、Bindingリクエストと読んでいます。
 例えば以下のような、依頼を出す事でしょう。 CL はサーバーへ「受け取ったサーバーから、レスポンスを返してもらう。」依頼をだす。CL はサーバーへ「受け取ったサーバーから、ポートだけ変えてレスポンスを返してもらう。」依頼をだす。CL はサーバーへ「受け取ったサーバーから、ポートとアドレスを変えてレスポンスを返してもらう。」依頼をだす。

 3のレスポンスを受け取る事ができたならば、「フルコーン」といえます。


○ 実際に送受信するメッセージ STUNではBindingリクエストと呼ばれるリクエストほをサーバーへサーバーへ送信します。

1. 送信先のアドレスとポートから返信するように依頼をだす。 { 0x00, 0x00,   // 最初の2byteは0 0x00, 0x01,   // Binding リクエストを意味する 0x01  0x00, 0x08,   // Attrinuteのサイズ 8バイトを表す 0x08 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // 16バイトのid 0x00, 0x03…

P2P探訪 STUNでNat越え その3

STUNサーバーをつくりながら、NATの構成を推測する方法を解説していきます。 ※ 制限のあるNAT配下で、通信をできるようにする方法については、後回しにします。 気になる方は「udp hole punching」とかで検索してください。
○もっとも厄介な制限 もっとも、厄介な制限はなんでしょうか? それは、UDPの使用に制限がかかっている場合です。 まずは、UDPの使用をできる事を確認してみましょう。

○サーバーに問い合わせて確認する  UDPの使用に制限があるかはアプリからは判断できません。なぜならば、 制限を加えているのは、主にルータだからです。なので、実際に外部のUDPサーバーと通信してみるより方法がありません。   外部のUDPサーバーに アクセスしてみて返答があれば、UDPが使える。返答がなければUDPが使えない。として判定できます。


○ 作った見よう  本書では、NAT越えをじょじょ広げていき、Stunにサーバーもどきを作っていきます。ただ、UDPが使用可能かのチェックをするのに必要な最小のこ構成は、「外部に返答を返すUDPサーバーを用意する」だけです。  早速用意してみました。
 やった事 Serversman で、vpsを借りる。     stunを実現するには、ipアドレスが2つ必要です。Standardプラン以降のものを準備する必要があるでしょう。もちろん、P2Pアプリとして実現するのであれば、Entryプランを2つ取得しても良いでしょう。http://kyorohiro.blogspot.jp/2013/07/blog-post.html
確認用に作成したコード サーバーから見えているクライアントのアドレスとポートを返すだけのアプリです。    https://github.com/kyorohiro/Hetimatan/blob/master/Hetimatan/src_nat/net/hetimatan/net/stun/HtunServer.java

○ 次回   Stunもどきの判定能力をじょじょにあげていたいと思います。次回はフルコーンNATかを判定してみる予定です。



P2P探訪 STUNでNAT越え その2

中継サーバーを間におく事で、お互いのアドレスとポート番号を特定する事ができまはた。UDPをSocketを使用して、通信してみましょう。しかし、残念ながら、多くの皆さんは通信に失敗する事でしょう。

<後述するような中継サーバー(P2PTracker)を試してみましょう>

ルータの制限なぜだ? UDPならばできそうなものだが? 「アドレスとポートから送り先をたどれない」ならば、そもそもUDPでの通信ができないではないか?
 そもそも、UDPはTCPと違いコネクションを持ちません。UDPは通信相手もUDPパケットに含まれるアドレスとポート番号を頼りにして相手と通信をします。
 ですから、「UDPパケットを受け取ったサーバー」と「そのサーバーからパケット情報をもらったクライアント」では、差が無いように思えます。


残念ながら、ルータによって制限がかけられています。 残念ながらルータには制限がかけられている場合があります。UDPの通信の仕組みはどうあれ、「UDPで送ったパケットを返信するのは、送った先の端末から」なのです。
ルータ制作者の立場にたってみれば、それ以外を想定する必要はないでしょう。


例えば、通信した「相手のアドレス」意外は不正な通信の可能性が高いとみなして、パケットを破棄する。(制限付きNAT)
例えば、「通信した相手のポート番号」意外は不正な通信の可能性が高いと見なして、パケットを破棄する。
※ 制限している訳では無くて、効率の問題でそうなっているだけかも知れません。

そもそも、UDPの本来の目的を満たすだけならば、送信相手によって、アドレスとポートを変えても良いでしょう。(シンメトリックNAT)

まとめ/次回予告 UDPに制限がある事を理解して頂けたことでしょう。 次回は、「制限を突破する方法」または、「制限の種類を特定する方法」について解説して行きたいと思います。



サンプルコード ○中継サーバーimport java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.Inet4Address; import java.net.UnknownHostException; import java.util.Linke…

P2P探訪 STUNでNAT越え その1

UPnPを用いて、NAT越えできました。しかし、ルータがUPnPをサポートしていなかったり。UPnPだけでは越えられないNATがあります。

本文では、その代案として前回解説できなかった。「適当なサーバーに接続してみて、相手から見えているアドレスを返してもらう方法」について解説していきます。

TCPの限界 インターネットで公開されている情報のほとんどは、TCPという通信方法でデータをやり取りされています。ですから、インターネットで情報を公開したい場合は、TCPサーバーを立ち上げる事を考える事でしょう。
 しかし、ルータがUPnPをサポートしていない場合、TCPを用いたサーバーを運用する事は困難になります。※ 基本、無理と考えもらって問題ありません。


接続相手から教えてもらう方法はどうした? 適当なサーバーに接続してみて、相手から見えているアドレスを返してもらう事で実現できないのでしょうか。前回はできそうな事を臭わせていました。しかし、TCPにおいて、これは困難です。

実際にTCPのプログラムを書き確認して見ましょう。接続相手のホストアドレスは推測できます。しかし、ポート番号を知るすべはありません。


import java.io.IOException; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; public class TCPTest { public static void main(String[] args) { TCPTest test = new TCPTest(); test.startServer(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } test.startClient(); } private Server mServer = new Server(); public void startServer() { mServer.start(); } public v…

P2P探訪 UPnPでNAT越えする

P2Pアプリは、サーバーとクライアントの両方機能をもったアプリです。基本的には、各言語のServer用のSocketでプログラムを書くことでこのサーバー部分の機能を実装できます。しかし、ご家庭の端末はそれだけでは実現できない事があります。


○  NATの弊害
 「端末から見えている自分のIP」と「通信相手から見えている自分のIP」がことなる場合があるからです。※ 異なるのが普通と事と考えてもよいでしょう。
 サーバーとしての機能を活用するためには、相手に自分のIPを伝える必要があります。そもそも、相手が自分のIPを知らないと、接続してもらえません。



○ 相手から見えているIPを知る方法 そこで、相手から見えているIPを調べて、相手に通知してあげましょう。そうすれば、サーバーとして機能を果たす事ができます。

相手から見えているIPを知る方法はいくつかあります。
* a. ルータに確認する
* b.適当なサーバーに接続してみて、相手から見えているアドレスを返してもらうぬ
などです。


ここでは、「a.ルータに確認する方法」について紹介します。


○ UPnPを使おう ルーターとは、UPnPプロトコルを通して会話する事ができます。

1 "239.255.255.250" 1900に参加する
例えば以下のような感じ
<pre>
MulticastSocket ssdpSocket = null; InetSocketAddress ssdpGroup = new InetSocketAddress("239.255.255.250", 1900); InetAddress nicAddress = InetAddress.getByName(hostName); ssdpSocket = new MulticastSocket(new InetSocketAddress(nicAddress, SSDP_PORT)); ssdpSocket.joinGroup(ssdpGroup, NetworkInterface.getByInetAddress(nicAddress));
</pre>
2. ルータを探す UPnPは同一ネットワーク(ルータ内)のコンピュータへ、「M-SEARCH」をブルードキャスト送信する事で実現で…