2013年03月08日
UDP hole punchingについて
そろそろ、やるやると言って後回しにしてきた事を書いていこうかと思いますよ〜
まずは、UDP hole punchingについてです
UDP hole punchingとは読んで字のごとく
UDPで穴をパンチしますww
わかりませんね笑
順をおって話をしましょう
ただ、自分も勉強中の身であるので解釈に間違いがあるかもしれません
多くの方がルータを使用してネットに接続しているのではないでしょうか?
雑な図ですがルータを使用している場合の図になります
そうすると図のようにPC1台ごとにプライベートIPアドレスが割り振られるのがわかります
(図だと逆にわかりにくいかもw)
グローバルIPアドレスはルータを指します
するとここで問題が起きます
それは、何回か前の記事に書いたソケット通信での問題です
ソケット通信では通信相手の「IPアドレス」を指定して通信を行います
ですがこの場合グローバルIPアドレスを指定しても
ルータまでは届きますが、ルータに属するどのPCに対するものなのかわからないので
パケットが破棄されてしまいます
かといってプライベートIPアドレスではインターネットを通じて通信を行うことはできません
しかしこれはルータに属するPCがサーバーの役割をする場合の問題です
なので、サーバーがグローバルIPで直接アクセスできるのならば問題ありません
TCPは常に接続した状態を維持します
図のPC1が外部サーバーに接続した場合
ルータはTCPのルートを保持するのでサーバーからデータを受け取ったときに
そのパケットが保持したルートである場合はしっかりとPC1にデータを届けてくれます
UDPでもTCPとあまり変わりません
UDPは送信して終わりの通信方式です
なのでルータはUDPを送信したポート番号を保持しておきます
そして、サーバーから送られてきたUDPの宛先ポートが保持したものと同じであれば
PC1にデータを送ります
では、P2Pのようにクライアント同士で通信する場合に
2つのクライアントがともに異なるルータに属する場合はどうでしょう
このような場合の接続問題を解決する方法の1つが
UDP hole punching であるのです
UDP hole punchingの原理はとても簡単です
それは先ほど説明したUDPにおけるルータの振る舞いを考えてください
ルータは自分が送信を行ったポートにパケットが届けば
返信が来たとしてPCまでデータを送ってくれます
すなわち、
自分が送信した相手と返信を行った相手が異なっていてもわからないのです
まずは図を見てください
この図で
Aはルータに属します
Bはグローバルなサーバーです
Cもルータに属します
前提条件として最低でもBとCはTCPで接続済みとします
まず、AはサーバーBにUDPを送信します
その後AのルータはBからの受信を受け付けます
そこでBは返信をするのではなくCにAのIPとポートを教えます
CはBから得たIPとポートでUDPを送信します
Cから送られたパケットをAのルータは先ほど送信したパケットに対する返信だと処理します
すると通常はできないCからAにUDPを送信することが可能になるのです
これはつまり
Aが返信受け付けるためにルータという壁に穴をあけておいて
Cがその穴にパケットを突っ込む
という感じですね笑
そうすると UDP hole punching の名前も納得がいきますよね
さてさて、ここまではPCを扱ってきましたが
実はAndroidでも全く同じです
調べたわけではないので確証はないのですが
Androidの3G回線やLTE回線での通信は
各キャリアの基地局というルータに属しての通信なのではないかと思うのです
なので、通常の方法ではAndroid同士の通信がうまくいかないのではないかと
しかし、Androidが基地局というルータに属していると仮定すれば
Android同士で通信を行う方法が見えてきますよね?
そう、UDP hole punching を使えば
Android同士でのP2P通信が可能になります
ただ、そのためにはグローバルなサーバーが必要になりますが
このグローバルというのは、グローバルIPアドレスでアクセスできるサーバーです
つまりルータのポート変換やらなんやらで自分のPCをサーバー化してもOKなんです
そこら辺の設定の仕方は調べてもらった方が早いと思いますww
手順としては
1、Android_A がサーバーにUDPを送信する
2、サーバーはAndroid_AのIPとポートをAndroid_BにTCPで送信する
3、Android_Bは受け取ったIPとポートあてにUDPを送信する
4、Android_Aは受信したパケットからIPとポートを取り出して使用する
5、Android_Bはサーバーから受け取ったIPとポートを使用する
6、Android_A B ともに相手のIPとポートを取得したのでP2Pを行える状態になる
となります
パケットからIPとポートを取り出す方法はJava APIなどを参照してもらえればすぐだと思います
Andoid同士の通信がどれほど需要があるのかわかりませんが
参考になるといいです(あくまで参考にして、しっかり調べて使ってください笑)
最後にSkySwiftでの通信モデルを載せておきます
ただ、企画段階での構想なので若干ことなりますw
まったく関係ないですが明日、というか今日(3月9日)は
オープンソースカンファレンス2013 Tokushima
が開かれます
最先端の技術にふれて、勉強のモチベーションを上げたいと思います
まずは、UDP hole punchingについてです
UDP hole punchingとは読んで字のごとく
UDPで穴をパンチしますww
わかりませんね笑
順をおって話をしましょう
ただ、自分も勉強中の身であるので解釈に間違いがあるかもしれません
多くの方がルータを使用してネットに接続しているのではないでしょうか?
雑な図ですがルータを使用している場合の図になります
そうすると図のようにPC1台ごとにプライベートIPアドレスが割り振られるのがわかります
(図だと逆にわかりにくいかもw)
グローバルIPアドレスはルータを指します
するとここで問題が起きます
それは、何回か前の記事に書いたソケット通信での問題です
ソケット通信では通信相手の「IPアドレス」を指定して通信を行います
ですがこの場合グローバルIPアドレスを指定しても
ルータまでは届きますが、ルータに属するどのPCに対するものなのかわからないので
パケットが破棄されてしまいます
かといってプライベートIPアドレスではインターネットを通じて通信を行うことはできません
しかしこれはルータに属するPCがサーバーの役割をする場合の問題です
なので、サーバーがグローバルIPで直接アクセスできるのならば問題ありません
TCPは常に接続した状態を維持します
図のPC1が外部サーバーに接続した場合
ルータはTCPのルートを保持するのでサーバーからデータを受け取ったときに
そのパケットが保持したルートである場合はしっかりとPC1にデータを届けてくれます
UDPでもTCPとあまり変わりません
UDPは送信して終わりの通信方式です
なのでルータはUDPを送信したポート番号を保持しておきます
そして、サーバーから送られてきたUDPの宛先ポートが保持したものと同じであれば
PC1にデータを送ります
では、P2Pのようにクライアント同士で通信する場合に
2つのクライアントがともに異なるルータに属する場合はどうでしょう
このような場合の接続問題を解決する方法の1つが
UDP hole punching であるのです
UDP hole punchingの原理はとても簡単です
それは先ほど説明したUDPにおけるルータの振る舞いを考えてください
ルータは自分が送信を行ったポートにパケットが届けば
返信が来たとしてPCまでデータを送ってくれます
すなわち、
自分が送信した相手と返信を行った相手が異なっていてもわからないのです
まずは図を見てください
この図で
Aはルータに属します
Bはグローバルなサーバーです
Cもルータに属します
前提条件として最低でもBとCはTCPで接続済みとします
まず、AはサーバーBにUDPを送信します
その後AのルータはBからの受信を受け付けます
そこでBは返信をするのではなくCにAのIPとポートを教えます
CはBから得たIPとポートでUDPを送信します
Cから送られたパケットをAのルータは先ほど送信したパケットに対する返信だと処理します
すると通常はできないCからAにUDPを送信することが可能になるのです
これはつまり
Aが返信受け付けるためにルータという壁に穴をあけておいて
Cがその穴にパケットを突っ込む
という感じですね笑
そうすると UDP hole punching の名前も納得がいきますよね
さてさて、ここまではPCを扱ってきましたが
実はAndroidでも全く同じです
調べたわけではないので確証はないのですが
Androidの3G回線やLTE回線での通信は
各キャリアの基地局というルータに属しての通信なのではないかと思うのです
なので、通常の方法ではAndroid同士の通信がうまくいかないのではないかと
しかし、Androidが基地局というルータに属していると仮定すれば
Android同士で通信を行う方法が見えてきますよね?
そう、UDP hole punching を使えば
Android同士でのP2P通信が可能になります
ただ、そのためにはグローバルなサーバーが必要になりますが
このグローバルというのは、グローバルIPアドレスでアクセスできるサーバーです
つまりルータのポート変換やらなんやらで自分のPCをサーバー化してもOKなんです
そこら辺の設定の仕方は調べてもらった方が早いと思いますww
手順としては
1、Android_A がサーバーにUDPを送信する
2、サーバーはAndroid_AのIPとポートをAndroid_BにTCPで送信する
3、Android_Bは受け取ったIPとポートあてにUDPを送信する
4、Android_Aは受信したパケットからIPとポートを取り出して使用する
5、Android_Bはサーバーから受け取ったIPとポートを使用する
6、Android_A B ともに相手のIPとポートを取得したのでP2Pを行える状態になる
となります
パケットからIPとポートを取り出す方法はJava APIなどを参照してもらえればすぐだと思います
Andoid同士の通信がどれほど需要があるのかわかりませんが
参考になるといいです(あくまで参考にして、しっかり調べて使ってください笑)
最後にSkySwiftでの通信モデルを載せておきます
ただ、企画段階での構想なので若干ことなりますw
まったく関係ないですが明日、というか今日(3月9日)は
オープンソースカンファレンス2013 Tokushima
が開かれます
最先端の技術にふれて、勉強のモチベーションを上げたいと思います
【ゲームプログラミングの最新記事】
投稿者:Sert|23:45|ゲームプログラミング
この記事へのコメント