(技術的な意味で)

もうちょっと気分とかでなんとかしたかったんです(技術的な意味で)

ネットワーク

UDPでデータ送信する:Java Applet編

UDPでデータ送信する方法のひとつに、Java Appletによる方法があったので、その方法を試してみた。
Java Appletについては最近の文献も少なく少し苦労した。たとえば、1度ブラウザでロードしたクラスファイルを再読込するには、Javaコンソールからキャッシュを明示的にクリアする点などはなかなか分からなかった。
特に署名付きAppletを作ったのははじめてだったので、そのあたりでも骨が折れた。

・コード
送信側コード。至って単純。

import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class SenderApp extends Applet {
	public void paint(Graphics g) {
		try {
			// 通信用ソケット準備
			DatagramSocket sock = new DatagramSocket();

			/*** UDPパケット送信 ***/
			// 送信文字列準備
			String sendString = "test message";
			byte[] sendBuffer = sendString.getBytes("JIS");	// 文字コードの指定
			// ソケット準備
			InetSocketAddress remoteAddress = new InetSocketAddress("127.0.0.1",13500);
			// UDPパケット作成
			DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, remoteAddress);
			
			// 送信
			sock.send(sendPacket);			
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
続いて受信側。これまた単純。
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class ReceiverApp extends Applet {
	public void paint(Graphics g) {
			try {
				
				/***** UDPパケット受信 *****/
				DatagramSocket receiveSocket = new DatagramSocket(13500);
				
				// 再利用可能にする
				// receiveSocket.setReuseAddress(true);

				// 受信バッファ
				byte[] receiveBuffer = new byte[100];
				// 受信パケット
				DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, 100);
				// 受信
				receiveSocket.receive(receivePacket);
				// 受信文字列格納
				String receiveString = new String(receivePacket.getData());

				// ソケットを閉じる
				receiveSocket.close();
				
				// 描画!
				g.setColor(Color.RED);
				g.drawString(receiveString, 30, 30);
				
			} catch (SocketException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
}
ソースコードは以上。コーディングの際には下記のサイトを参考にした。

Javaの道 >  掲示板 >  回答(JavaアプレットからUDPにメッセージ送信したい)
http://www.javaroad.jp/bbs/answer.jsp?q_id=20060907104644159

【コラム】 ライトニングJava
http://journal.mycom.co.jp/column/java/052/index.html

・署名付きAppletの作成
はじめてだったので検討もつかなかった作業。
以下のサイトが詳しい。ちなみに私は実はJDKが入っていなくてまずそのインストールからはじめました。。。

Java RADKRAFT: Java Applet + SignatureApplet マイク
http://kamakura.cool.ne.jp/oppama/misc/java-plugin-sig.html

・CLASSファイルではなくJARファイルを読み込むHTMLの書き方
テクニカルQ&A  Java 04 - アプレットで .zip や .jar ファイルを使う
http://developer.apple.com/jp/qa/java/java04.html

盲点。
先日の記事で「JobsはJavaが嫌い~」とか言っていたのに、皮肉なことにAppleのWebサイト。しかも12年前の記事。笑。

・CLASS/JARファイルを更新したらキャッシュを明示的にクリア
以下のサイトがなかったらずっとはまっていたと思う。
Java Appletの記事で2005年は比較的新しい。。笑。

Java Primer: アプレットの再読み込み
http://mibai.tec.u-ryukyu.ac.jp/~oshiro/Doc/java_primer/AppletReload.html

とりあえず、これらの情報を参考に上記のコードからCLASSファイル、JARファイル、keyによる署名を行ったところ、localhost同士ではうまく通信できた。
あとは実際にNATを越えてUDP通信する方法について考える必要がある。
試しにネットワーク外部の人と通信を試みたが失敗。。。NATの設定の問題によると思うので、ひとまず固定IPの相手と通信が確立した後、改めて検討したい。

この記事についてTwitterでつぶやく

UDPパケットを送信する方法まとめ

先日からFlashのActionScriptを使ってUDP通信をしようとしていたが、用意されている仕組み(RTMFP)ではSymmetric NATを越えられない(そして我が家はSymmetric NATの下にいるw)ために一旦保留。
他にUDPを使って通信する方法を色々と考えてみたのでまとめ。

言語UDP
通信
プラットフォーム備考
ブラウザ
組み込み
WindowsMaciPhone/iPad
Flash / ActionScript×Symmetric NATを越えられない
Java Applet×時代遅れ感も...
Adobe Air××UDP通信は最近サポートされたらしい
ネイティブアプリ
(C#でつくるとか)
×OSごとに作る必要あり確実だけど面倒な方法ではある...

こんなところか。

RTMFPがSymmetric NATを越えられれば完璧なんだが。片側がSymmetric NAT下でもう片方が違う環境にいたらどうなるのだろうか。一方的な送信なら出来るのか、など、ちょっと試してみる必要があるかも。
Flashの派生版的な位置づけであるAdobe AirではUDP通信が(Air 2.0から)可能になったらしい

時代遅れ感が満載だがJava Appletなら色々な要求を満たすことができる。Googleで情報を検索しても2002-2004年くらいの情報しか出てこないが。。。笑。

ちなみにネットで調べていたらSteve JobsはJavaが嫌いらしく、iPhone上でJavaがサポートされる可能性はまずないとのこと。Flashについて将来的なサポートの可能性が0ではないかも。。。そのあたりも加味すると選択が難しくなる。

Java Appletの復習(授業でやったのが5年前、プライベートではiアプリがではじめた2002年以来か)と、RTMFPのNAT対応について個別に調べるあたりから、手をつけようと想う。

この記事についてTwitterでつぶやく

Amazon EC2 AMIの保存で一苦労

Amazon EC2では、インスタンス(AMI)をシャットダウンするたびに設定内容が失われてしまう。
だから、設定を色々と変更したインスタンスのAMIを「保存」しておいて、それを呼び出す、という形をとる。

そのAMIの保存が、難航したので書いておく。
手順は以下の通り。

  1. AWS Management Consoleにアクセス
  2. S3のタブから、イメージを保存するバケットを作成。ほかの人も閲覧可能なパーミッションを設定しておく
  3. SSHなどでインスタンス上のサーバにログインし、AMIイメージの作成を行う。このとき(イメージ名).manifest.xmlが作られる。
  4. 作成したイメージをS3上のバケットに転送する
  5. インスタンス上のサーバからログアウトし、AWS Management Consoleからインスタンスを終了させる
  6. AWS Management ConsoleからAMIを選び、Register New AMIする。URLにはバケット名/(イメージ名).manifest.xmlを指定。
  7. 完了!

赤字にしたステップで躓いた。

前者のパーミッションの設定をしていなかったせいでそもそもつまづいていたのだが、それに気づかず。
AWS Management ConsoleからAMIの保存を使用としたが、URLの指定方法がわからない。勘でバケット名/イメージ名.manifest.xmlと打ち込むが、403 Forbiddenと言われる。これがパーミッションのせいだったんだが、気づかず、そもそもAWS Management ConsoleからはAMIの保存が出来ないと思いんでしまった。

その結果わざわざEC2 API Toolsをダウンロードしてしまった。その環境設定などに戸惑った挙句、結局パーミッションが設定出来ていないから403 Forbiddenが出ていることに気づく(遅い)。
適切に設定したところ、EC2 API Toolsから無事にAMIの保存が出来た。これならきっとAWS Management Consoleからも出来たはず。

なんだし。



この記事についてTwitterでつぶやく

Amazon EC2をはじめてみた

itworks


前々からAmazon EC2は気になっていたのだが、ついに踏ん切りをつけようとデビューしてみた。
最初は何も分からなかったが、試行錯誤の結果、概念としては以下のような手順を踏めばよいことがわかった。

とりあえずの目標として、何も無い状態からはじめて、「固定ドメインでHTTPサーバを公開」することを目標としてみた。以下はそのための手順。
  1. Amazon Web Servicesのアカウントを作成する
  2. Amazon EC2に申込む
  3. Amazon EC2上に新しいインスタンスを作成し、走らせる
  4. 作成したインスタンスにSSHなどでアクセスするための秘密鍵を取得する
  5. 作成したインスタンスのセキュリティ設定を行う(ポートの開放など)
  6. 作成したインスタンスにSSHでアクセスし、環境設定(Apacheのインストールなど)
  7. DDNSサービスを利用してIPアドレスとドメイン名を結びつける
  8. Webブラウザから取得したドメイン名を入力し、作成したインスタンス上のWebページを閲覧!目標達成!
と言ったところ。
最初はAmazon EC2とAmazon S3の関係もいまいちわからず困っていたが、どうやらなんとかうまくいった。
実際の手順は以下のサイトが非常に参考になる。

利用方法(EC2ログインからインスタンス起動まで)
http://www.incharge.jp/howtouse2

Amazon Web Servicesの画面は更新頻度が高いらしく、このようなWebサイトのスクリーンショットと実際の画面が一致しないことも多い。
そのあたりはなるべく最新の記事を参照するか、単語を拾って気合で頑張るしかない、かと。

私は何箇所かつまづいたところがあった。その際に参考になったサイトとかをメモしておく。

5. 作成したインスタンスのセキュリティ設定を行う(ポートの開放など)

あたりまえだけど、22番ポートを開けないとSSH接続できないし、80番ポートを開けないとHTTP接続できない。
けどまぁ当然最初ははまるわけで。あれ?SSHつながらないじゃん、とか。
そのへんは以下のサイトで確認した。



6. 作成したインスタンスにSSHでアクセスし、環境設定(Apacheのインストールなど)

明示的にDLした秘密鍵を使ってSSH接続したことが今までなかったので、接続方法がいまいち分からず。
特に、私の好きなターミナルであるPoderosaでは、Defaultで生成される秘密鍵はそのままでは使えず、PuttyGenを用いて一旦変換しないといけない、という情報は検索しなければわからなかった。
以下がその情報源。

PoderosaをつかってAmazonEC2に接続
http://saifis.net/?p=116

PuttyやWinSCPでSSH接続する際にもPuttyGenを用いて変換した秘密鍵が必要とのこと。難しいな。

7. DDNSサービスを利用してIPアドレスとドメイン名を結びつける
これもはじめてやったのだが、Dynamic DNSサービスを利用してみた。
特定のドメイン名と、ころころ変わるIPアドレスをヒモ付てくれる便利なサービス。DHCPのこっち側にある自宅サーバとか、今回のAmazonEC2上のサーバみたいに、グローバルIPがころころ変わる場合にとっても便利。
調べたら無料のDDNSサービスがいくつかあるみたいなので、Dynamic DO!.jpという日本のサービスを利用してみた。

Dynamic DO!.jp        
https://ddo.jp/

おかげでhttp://アカウント名.ddo.jp/というアドレスが取得できた。アカウント名は内緒。

あと、Amazon EC2のインスタンス上でグローバルIPアドレスを取得するには、下記のコマンドを用いるらしい(出典:Amazon EC2で個人サイトの作成、ボチボチ続けてます。)。
# curl http://169.254.169.254/latest/meta-data/local-ipv4

イメージとしては # ifconfig で済むかと思っていたけど、グローバルIPの取得だもんね。ifconfigじゃローカルIPしか取得できんのかね。



この記事についてTwitterでつぶやく

ActionScriptでP2Pするための勉強:NAT越えとSkype

ActionScriptを使ってP2P通信するアプリを作ろうとしている件。

RTMFP(Real-Time Media Flow Protocol)というプロトコルが用意されており、アプリケーション上でAdobeの認証サーバにアクセスし、near IDという認証IDをもらえば簡単に相互通信できるはず、だった。
RTMFPを使った通信の実現については下記が詳しい。

Flash Player上でP2P通信ができるRTMFPについて
http://blog.katsuma.tv/2008/12/about_rtmfp.html

しかし実際に僕と友人の家で試してみると、通信できない。。。
詳しく調べてみると、こんな記事が。

SkypeのNAT越えについて
http://gravit.blog.so-net.ne.jp/2009-06-17

どうやらRTMFPではNAT越えできないらしい。そこでNATについて詳しく調べてみた。
図を用いて考える。
NAT

図の左側がローカルの世界、右側がグローバルの世界で、
ネットワークの境目(Router)でNATとFirewallがお仕事をしている。
丸付き数字の1~4は、AさんがFacebookにアクセスするときのパケットの流れを示す。
AさんのコンピュータからNATを経由してFacebookにリクエストが送られ、FacebookからNATを経由してAさんのコンピュータにレスポンスが送られる。

ここでNATの役割はローカルIP(赤字)をグローバルIPに変換すること。
その時の変換のしかたにより分類があり、NAT越えの簡単・難しいもそれによって決まる。
AさんのコンピュータがGoogleとFacebookにアクセスしようとする例を用いると、ざっくり以下のようになる。

・Full Cone NAT
Googleに送信するときもFacebookに送信するときも、同じ{グローバルIP:ポート番号}を用いる。

・Restricted Cone NAT
上記と同様だが、Firewallの働きで、一度送信したことのあるIPアドレスからのパケットしか受け取らない。
例えばGoogleしかアクセスしたことないのに、Facebookからパケットが送られてきたら、このタイプのNATはそのパケットを遮断する。

・Port-Restricted Cone NAT
上記と同様だが、Firewallの働きで、一度送信したことのあるIPアドレスの、送信した先のポートからのパケットしか受け取らない。
例えばGoogleのWebサイト(ポート番号80)にしかアクセスしたことないのにGoogleのほげほげサービス(ポート番号65432)からアクセスが来たら、このタイプのNATはそのパケットを遮断する。

上記の3タイプはUDP Hole Punchingで越えられるっぽい。

・Symmetric NAT
Googleにアクセスするときと、Facebookにアクセスするときで、用いる{グローバルIP:ポート番号}が異なる。
このうちグローバルIPが毎回変わるタイプ(複数アドレスSymmetric)と、グローバルIPは毎回同じタイプ(単一アドレスSymmetric)NATがあるらしい。

このSymmetric NATがあると、UDP Hole Punchingが効かないからやっかいみたいだ。

※忘れないように書いておくと、Googleをログインサーバ、Facebookを通話したい相手だと思う。Googleにアクセスしてきた際のAのコンピュータの{グローバルIP:ポート番号}と、Facebookにアクセスする際の{グローバルIP:ポート番号}が違ったら。。。
GoogleがFacebookに「Aのコンピュータの{グローバルIP:ポート番号}これだぜ!だからこれに向かってUDP飛ばせよ!」と教えてUDP Hole Punchingを斡旋しても、Facebookにアクセスする際は{グローバルIP:ポート番号}じゃないから、Facebookから教えてもらった{グローバルIP:ポート番号}に通信飛ばしても哀しいかな破棄されるわけだ。


自分の環境がどのタイプのNATなのかは(もしくはNATを用いていないのか)は、下記のAdobeのWebサイトを用いると判定できる。

RTMFP Connectivity Checker
http://cc.rtmfp.net/

各項目の解釈については下記。
http://help.adobe.com/ja_JP/flashmediaserver/devguide/WSa4cb07693d123884520b86f312a354ba36d-7ff5.html#WSa4cb07693d123884520b86f312a354ba36d-7fef


ここまで整理して生じる疑問は、じゃあSkypeはどうやってピア間の接続を実現しているのか、ということ。
おそらくNAT下にいないユーザ(Skype関連の記事ではよくSuper Nodeと呼ばれる)をTURNサーバとして機能させていると思われる。
ようは、互いのクライアント同士をつなぐのではなく、互いのクライアントがそれぞれSuper Nodeとの間に接続をはり、そのSuper Nodeを介して通話をしている、のではないか。
この接続はなるべくUDPにすることで、Super Nodeの負荷を軽減している、とする記事もあった。外向きのUDPがそもそも全然許可されていない環境だったら、TCPで接続はるみたい。
ポート番号80(http)とかでFirewallをだます、という古典的な手法を用いている様子。

というわけで、ActionScriptでもSymmetric NATを越えて通信を行う場合には、TURNサーバ的な別の誰かを経由して通信させることになるのかな。。。
僕の今の環境はさっきのCheckerでみたらSymmteric NATみたいだから、通信アプリケーションをつくろうと思うとその攻略からだな。。。


本文中で引用しなかった参考文献はこんなところ。

NAT越えに関する技術とその仕組み
http://www.skame.nu/P2Pmeeting/0409sunouchi.pdf
個別の仕組みの説明が丁寧。

P2P通信技術:NAT越え ~STUNとUPnPと、時々、TURN~
http://homepage3.nifty.com/toremoro/study/voip2008/NATTraversal.pdf
個別の仕組みの説明が丁寧。

NAT越えの方法
http://ynomura.dip.jp/archives/2006/06/nat.html
Overview。全部のイメージがだいたいついてから見るととってもわかりやすい。


次回はこれを扱うかも? RTMFPとの比較になるかな。
Flex/Action ScriptでUDPプログラミング
http://taxpon.com/?p=688

というわけで長くなったけどActionScriptでP2P通信するための前提知識として、NATの種類とその対応についてでした。


この記事についてTwitterでつぶやく
Profile

Akira

記事検索
Twitter
Access



    • ライブドアブログ