March 29, 2011

Java Hello World on Ubuntu Linux






新しくプログラミング言語を学習始める時には、書いたコードの実行結果をその都度確認出来ることが大切です。出来れば、一行一行試せる環境が理想的です。Eclipse等のIDEを利用すると非常に便利。

LinuxのCLIコマンドレベルでも出来ることは多いので、Javaでそれを試してみた時のメモを残しておきます。


JDKのインストール



Javaの開発環境(JDK)をインストールしましょう。

$ sudo apt-get install openjdk-6-jdk



Javaのソースコードをコンパイル



簡単なコードを書いて、クラスファイルにコンパイルしましょう。

HelloJava.java ファイル
public class HelloJava {
public static void main(String [] args) {
System.out.println("Hello Java");
}
}


$ javac HellJava.java



HelloJava.classというクラスファイルが生成されますので、実行してみましょう。

$ java HelloJava
Hello Java




インタプリタを使ったコードの実行



インタプリタを使うとコードを都度解釈して実行してくれます。非常に短いコードのテストをするときには役立つと思います。JavaについてはBeanShellというものを利用します。

$ sudo apt-get install bsh
(snip)
$ bsh
BeanShell 2.0b4 - by Pat Niemeyer (pat@pat.net)
bsh % System.out.println("Hello Java");
Hello Java



BeanShell上で好きなモジュールをimportしたり、クラスを定義したり、ひと通りの事が出来るかと思います。

March 24, 2011

tshark (WiresharkのCLI版) の使い方






tsharkWiresharkのCLI(コマンドライン)版と言えるもので、GUIのないサーバ上やターミナルからtcpdumpと同じ利便性で使える、パケットキャプチャです。tsharkによるSummaryもWiresharkど同等の内容で確認できるので、tcpdumpよりtsharkの方が好まれる人も多いのでは。

GUIではWiresharkが最もメジャーなパケットキャプチャで、使用されている方も多いかと思います。かたやCLIではtcpdumpを使う方が多いのではないでしょうか。この場合、tcpdumpの代わりにtsharkを使うとCLIでもWiresharkと同様の確認が行えるのでよいかもしれせん。

tsharkはWiresharkのパッケージに含まれて提供されていますので、Wiresharkがインストール済みであれば既に実行可能かと思います。
WindowsやLinuxでは下記Pathにあると思います。
Windows: C:\Program Files\Wireshark\tshark.exe
Linux: /usr/bin/tshark

もし無ければ、Linuxであれば個別にインストールも可能です。サーバ向けにインストールを行なっている環境だと、個別インストールが必要かと思います。Ubuntuであれば下記のように。
$ sudo apt-get install tshark



Windowsであれば基本的にWiresharkを使える環境にありますので、これ以降の使い方についてはLinux(Ubuntu)ベースにしています。
また、tsharkの使い方はWindowsのコマンドプロンプトで使う場合にも基本的には同じです。tsharkのバージョンは1.2.7を想定しています。

後述する -z を工夫してLinuxシェル上で扱うことで、ケースによっては非常に強力なツールになるかもしれません。


tsharkはroot権限で実行しなければならない



パケットキャプチャを行うときには、該当インターフェースをプロミスキャスモードで動作させなければなりません。それにはroot権限が必要になるので注意しましょう。

非root権限で実行すると以下のようなエラーになります。以降の -D オプションもききません。
$ tshark
tshark: There are no interfaces on which a capture can be done



tsharkでインターフェースの指定



-D オプションで使用可能なインターフェースとそのIndexを確認することが出来ます。まずはこれを実行しましょう。

$ sudo tshark -D
1. eth0
2. any (Pseudo-device that captures on all interfaces)
3. lo


-i オプションでパケットキャプチャを行うインターフェースを指定します。指定しなければ一番Indexの若いインターフェースになります。下記の例では全てeth0インターフェースでキャプチャを開始します。

$ sudo tshark
$ sudo tshark -i 1
$ sudo tshark -i eth0



-f オプションでキャプチャフィルタの指定が出来ます。tcpdumpやWiresharkと同じくlibpcapフォーマットのフィルタ構文を使用します。クォーテーションでくくって指定してください。詳しいフィルタの構文についてはググってください。

$ sudo tshark -i eth0 -f 'port 80 and host 10.0.0.1'


なお、-R オプションはディプレイフィルタなので、標準出力に対してのフィルタオプションです。キャプチャファイルを保存しない一時的な実行であれば、標準出力への出力内容は同じなります。-f オプションの方がマッチしないパケットをバッファリングしない分、システムに優しい気もしますが。


tsharkでキャプチャファイルの保存と読み込み



-w で指定したPATHにキャプチャファイルを保存します。-r で指定して読み込むことが出来ます。また、Wiresharkで直接読み込むことができますので、サーバ上でtsharkで保存したファイルを、共有ディレクトリを通じてクライアントPCのWiresharkで確認する方法をよくとります。

$ sudo tshark -i eth0 -f 'port 80' -w /mnt/share/test.cap



tsharkで詳細表示



-V を追加で指定すると、Wiresharkで見慣れた内容が各パケットエントリーごとで確認できます。ただ、内容量が多いのでパケット数が少ない場合に限られると思います。

$ sudo tshark -i eth0 -f 'icmp' -V



tsharkで名前解決を無効にする



IPアドレスやPort番号を、FQDNやサービス名に変換表示させたくない場合もあるかと思います。その時には -n オプションを付与しましょう。
$ sudo tshark -i eth0 -n



-z オプション



-z をキーに、tsharkの動作を強力にカスタマイズ出来ます。-z に続けて様々なオプションでそれらを指定します。


-z オプションでInfo Columnのカスタマイズ



man tsharkより抜粋
-z proto,colinfo,filter,field

Append all field values for the packet to the Info column of the one-line summary output. This feature can be used to append arbitrary fields to the Info column in addition to the normal content of that column. field is the display-filter name of a field which value should be placed in the Info column. filter is a filter string that controls for which packets the field value will be presented in the info column. field will only be presented in the Info column for the packets which match filter.

NOTE: In order for TShark to be able to extract the field value from the packet, field MUST be part of the filter string. If not, TShark will not be able to extract its value.


Info Columnとは、tshark実行時に標準出力に出力される、各パケット毎のSummary情報部分です。この出力内容をカスタマイズすることが出来ます。
注意すべき点は、この場合の -z オプションはディプレイフィルタではないので、このfilterにマッチしなくてもInfo columnに表示しないだけです。マッチするものだけを表示させたい場合は、-R オプションでのディスプレイフィルタを併用する必要があります。
下記にいくつか例を挙げます。

# TCP Window SizeをInfo Columnに追加表示します。
$ sudo tshark \
-z "proto,colinfo,tcp.window_size,tcp.window.size" \
-r test.cap

107 0.375142 124.83.141.28 -> 10.0.0.2 TCP http > 52571 [ACK] Seq=387 Ack=1155 Win=65535 Len=0 tcp.window_size == 65535


-z オプションは複数指定して、Info Column上で連結することが出来ます。この例ではディスプレイフィルタでHTTP Request Methodが含まれているものだけをフィルタしています。
$ sudo tshark \
-z "proto,colinfo,http.request.uri,http.request.uri" \
-z "proto,colinfo,tcp.window_size,tcp.window_size" \
-r http.cap -R "http.request.method"

79 1.693395 10.0.0.2 -> 124.83.183.212 HTTP GET /oi?t=j&s=ytop_ams_rmdl_utf8&i=toppage HTTP/1.1 tcp.window_size == 65536 http.request.uri == "/oi?t=j&s=ytop_ams_rmdl_utf8&i=toppage"


-R オプションも利用して"http.content_type"の指定があるレスポンスを抽出し、"http.content_encoding"をInfo Columnに表示します。
$ sudo tshark \
-z "proto,colinfo,http.content_encoding,http.content_encoding" \
-R "http.content_type" \
-r http.cap

51 0.113339 124.83.235.204 -> 10.130.141.230 HTTP HTTP/1.1 200 OK (text/html) http.content_encoding == "gzip"
66 0.166352 118.151.253.60 -> 10.130.141.230 HTTP HTTP/1.1 200 OK (JPEG JFIF image)
78 0.180127 202.93.69.243 -> 10.130.141.230 HTTP HTTP/1.1 200 OK (JPEG JFIF image)
89 0.201872 124.83.223.251 -> 10.130.141.230 HTTP HTTP/1.1 200 OK (GIF89a)
93 0.227038 114.111.103.164 -> 10.130.141.230 HTTP HTTP/1.1 200 OK (application/x-javascript) http.content_encoding == "gzip"
104 0.356011 124.83.141.28 -> 10.130.141.230 HTTP HTTP/1.0 200 OK (GIF89a)


例えば、DNSのCNAMEレコードが返ってきた数を計測します。
$ sudo tshark \
-z "proto,colinfo,dns.resp.type,dns.resp.type" \
-r dns.cap | egrep CNAME | wc -l

2



-z オプションでプロトコル統計をとる



man tsharkより抜粋
-z io,phs[,filter]

Create Protocol Hierarchy Statistics listing both number of packets and bytes. If no filter is specified the statistics will be calculated for all packets. If a filters is specified statistics will be only calculated for those packets that match the filter.

This option can be used multiple times on the command line.


単純に以下のように実行してみてください。Ctrl+Cで終了させるとプロトコル種別ごとの統計が取れます。
$ sudo tshark -i any -z io,phs
(省略)
=====================================
Protocol Hierarchy Statistics
Filter: frame

frame frames:29 bytes:4624
sll frames:29 bytes:4624
ip frames:18 bytes:3960
tcp frames:18 bytes:3960
ssh frames:10 bytes:3464
arp frames:11 bytes:664
=====================================



-z オプションでトラフィック統計をとる



man tsharkより抜粋
-z io,stat,interval[,filter][,filter][,filter]...

Collect packet/bytes statistics for the capture in intervals of interval seconds. Intervals can be specified either as whole or fractional seconds. Interval can be specified in ms resolution. If Interval is 0, the statistics will be calculated over all packets.

If no filter is specified the statistics will be calculated for all packets. If one or more filters are specified statistics will be calculated for all filters and presented with one column of statistics for each filter.

This option can be used multiple times on the command line.


これも単純に実行してCtrl+Cで終了することで、その効果を確認することが出来ます。intervalごとのトラフィック統計を取れます。
$ sudo tshark -i any -z io,stat,1
(省略)
=================================
IO Statistics
Interval: 1.000 secs
Column #0:
| Column #0
Time |frames| bytes
000.000-001.000 2 124
001.000-002.000 2 346
002.000-003.000 4 788
003.000-004.000 7 860
004.000-005.000 2 618
005.000-006.000 2 346
006.000-007.000 3 408
007.000-008.000 4 742
008.000-009.000 4 566
009.000-010.000 5 76
=================================



まとめ



基本的な使い方はほぼtcpdumpと同じで、実際は同じpcapライブラリを使用しているので、内部的にも同じように動作する部分が多いです。Wiresharkの仕様にあわせてサーバ上でのパケットキャプチャを取得したい場合は、tcpdumpよりtsharkを使用する方がベターかもしれません。

特に、 -R オプションや -z オプションを使用することで、tsharkは強力にあなたをサポートできるでしょう。

他にもいくつかWiresharkに組み込まれている技術をtsharkでも利用できますので、興味があればman tsharkなどから勉強すると良いと思います。

March 23, 2011

PerlとApacheでChunked EncodingなResponseを返す





通信テストの目的でChunked EncodingなHTTP Response Trafficを流すために必要だったことを書き残します。


[Topology]
Client---Server

[Client]
Windows 7
Firefox 3.6.15

[Server]
Ubuntu 10.04
Apache: 2.2.14
Perl: 5.10.1


Chunked EncodingはResponseのContent-Lengthが決まっていない場合に用いるもので、CGIなどによって動的にコンテンツが変化する場合やストリーミング等のコンテンツはウェブサーバは通常Chunked EncodingでResponseを返そうとします。
コネクション接続時にコンテンツの終端(Content-Length)が決定していないので、持続接続を継続する必要があります。そのためにConnectionヘッダにkeep-aliveを指定する必要があります。
また、これらの条件をカバーするためにはHTTP 1.1でリクエストする必要があります。

私の環境では一点引っかかってChunkedで返さない状況がありました。
Apacheでmod_deflateが有効になっていて、Transfer-Encoding: gzipで返すようになっていました。
mod_deflateはテキストデータなど圧縮することで転送効率が高まるケースでは有効なmoduleで、クライアントのリクエストにもAccept-Encodingヘッダで許容しているケースが多いかと思います。

この場合、Apacheがレスポンスデータをgzipでアーカイブします。サーバ上の動的処理が完了しgzipで圧縮され、Content-Lengthが確定し、下記のようにgzip形式のデータがクライアントに返ってきます。例はWiresharkのFlow TCP Streamで、赤ハイライトがクライアントリクエスト、青ハイライトがサーバレスポンスです。Content-Lengthがついてレスポンスが返って来るので、Chunkedにはなりません。



GET /cgi-bin/comfort.pl HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 ( .NET CLR 3.5.30729; .NET4.0C)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 115
Connection: keep-alive
Cache-Control: max-age=0


HTTP/1.1 200 OK
Date: Wed, 23 Mar 2011 07:59:17 GMT
Server: Apache/2.2.14 (Ubuntu)
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 38
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/plain

..........3.R(I-..2..FP..J.@i....?(...




このgzip Encodingを無効にするため、Apacheの設定で除外させる必要があります。
一例として、User-Agentの文字列マッチングで条件を絞ります。変更するファイルは /etc/apache2/sites-available/default にあたる設定ファイルの、該当するDirectoryです。"Mozilla"から始まるUser-Agentにマッチするリクエストに対して、"no-gzip"を適用させます。(ファイルタイプでマッチングさせる方法等、いくつかの方法があります)

<Directory "/usr/lib/cgi-bin">
  AllowOverride None
  Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
  Order allow,deny
  Allow from all
  BrowserMatch ^Mozilla no-gzip
</Directory>


これで、私の環境ではChunked Encodingでレスポンスが返るようになりました。


GET /cgi-bin/comfort.pl HTTP/1.1
Host: 10.0.0.1
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 ( .NET CLR 3.5.30729; .NET4.0C)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 115
Connection: keep-alive
Cache-Control: max-age=0


HTTP/1.1 200 OK
Date: Wed, 23 Mar 2011 07:56:23 GMT
Server: Apache/2.2.14 (Ubuntu)
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/plain

8
0: test

8
1: test

8
2: test

8
3: test

8
4: test

0




ちなみにサーバ側でPerlスクリプトを書きました。イテレータでChunkを好きな頻度で返してます。

#!/usr/bin/perl
$| = 1;

print "Content-type: text/plain\n\n";
for $x(0..4) {
print "$x: test\n";
sleep 5;
}

exit 0;

$| はPerlの特殊変数で、0以外の値の時にSTDOUTのAuto Flushが実施されます。0(Default)の場合はサーバ側でレスポンスがバッファリングされます。
テスト目的のためには継続的なレスポンスが欲しかったので、Perlでのバッファリングも回避させておきます。



余談ですが、Firefox(3.6.15)でHTTP Requestの仕方で、ヘッダに変化があります。

URLフォームにURLを入力してEnterを押す方法とは別に、Reloadを実行すると下記ヘッダが付与されます。
Cache-Control: max-age=0

Shiftを押しながらReloadボタンを押すと、下記ヘッダが付与されます。
Pragma: no-cache
Cache-Control: no-cache


Controlを押しながらでは何も変わりません。

参考: What's the difference between Cache-Control: max-age=0 and no-cache ?