Internetworking
awk4j
(AWK for Java Platform)
awk4j の入力ファイルは、 URL 指定による
一般ファイルと同じインターフェイスでの読み込みが可能である。
さらに、ソケット通信機能を利用することにより、TCP/IP, UDP での双方向通信が可能となる。
特殊ファイル名:
"/inet/PROTOCOL/LOCAL-PORT/REMOTE-HOST/REMOTE-PORT"
- PROTOCOL:
- 通信プロトコルを指定する (tcp または udp)
- LOCAL-PORT
- このコンピュータのローカルポートを使用して通信する
(TCP/IP ならサーバとして動作)
- REMOTE-HOST
- 接続先の IP アドレス または ホスト名
- REMOTE-PORT
- 接続先のポート番号
双方向パイプ:
|&
- print[f] |&
特殊ファイル名
- 特殊ファイル名 |&
getline の形式で使用する。
daytime
は人が読める形式で時間を出力するサービスで、現在の時刻をテキスト形式で返す。
公開NTPサーバにアクセス
make
daytime
${AWK4J} -f Daytime.awk /inet/tcp/0/time.xmission.com/daytime
Sat Jul 28 19:51:02 2007
BEGIN { # daytime
Client
inet = ARGV[1]
while (0 < (inet |& getline)) {
print
if (NF)
break # 空行以外のデータ受信で接続を閉じる
}
close(inet)
}
UDPディレクティッド・ブロードキャストで送信
UDP通信で、ホスト・アドレス部を全て 1 (255)
とし近所のネットワークへブロードキャストすることにより、複数クライアントで同時受信できる。
make
daytimeServerUDP # UDPサーバ
${AWK4J} -f DaytimeServer.awk /inet/udp/0/192.168.0.255/8813
make daytime
# UDPクライアント
${AWK4J} -f Daytime.awk /inet/udp/8813/localhost/0
Sun Jul 29 11:12:02 JST 2007
BEGIN { # daytime
Server
inet = ARGV[1]
while(1) {
printf "" |& inet
# 空文字を送信してクライアントとの接続を待つ
print
strftime() |&
inet
close(inet)
system("sleep 10")
}
}
Webサーバとクライアント (ブラウザ) 間で、
ウェブページを送受信すプロトコルで、要求メッセージに対して応答メッセージが返される。
検索エンジンにアクセス
検索エンジンにアクセスして、検索結果を表示する
make http
${AWK4J} -f Http.awk www.google.co.jp '/search?q=awk4j'
BEGIN { # http
Client
host = ARGV[1]
path = ARGV[2]
inet = "/inet/tcp/0/" host "/80/"
printf
"GET %s%s HTTP/1.0\r\n\r\n",
host, path |& inet
while (0 < (inet |&
getline))
print
close(inet)
}
httpサーバ
固定メッセージを返す、簡単な httpサーバの例を下記に示す。
make http
${AWK4J} -f HttpServer.awk /inet/tcp/8880/0/0
ブラウザで http://127.0.0.1:8880/ にアクセス
BEGIN { # http
Server
inet = ARGV[1]
RS = "\r\n"
while(1) {
while (0
< (inet |& getline line)
&& ""
!= line) # リクエストを受信
print line
html =
"<html><head><title>simple
httpd</title></head><body>" \
strftime() " hello, http
world.</body></html>"
header =
"HTTP/1.1 200 OK" RS \
"Connection: close" RS \
"Content-Type: text/html" RS \
"Content-Length: " length(html) RS
printf
header RS html |& inet #
レスポンスを送信
close(inet)
}
}
サーバの多重スレッド化
Note: echoサーバを、awk4j 拡張機能 (試験実装)
を使用して、多重スレッド化する例を下記に示す。
make echoServer
${AWK4J} -f EchoServer.awk /inet/tcp/8807/0/0
make echoClient
${AWK4J} -f Echo.awk /inet/tcp/0/0/8807
BEGIN {
# echo Server
inet = ARGV[1]
T[1] =
system("Thread", "echo") #
指定した関数 echo()を新規スレッドで実行
T[2] =
system("Thread", "echo") #
system("join",
T)
# スレッドの完了を待ち合わせる
}
function echo(
line)
{ #
この関数は多重スレッドで動作する
while(1) {
while (0
< (inet |& getline line)
&& "" != line) {
print
line |& inet
#
空行を受信するまで応答を返す
}
close(inet)
}
}
Note: 多重スレッド環境では、グローバル変数($0など)
への書き込みは避け、ローカル変数を使用する。
BEGIN { # echo
Client
inet = ARGV[1]
for (i = 1; i <= 3; i++) {
print ++seq " " strftime() |& inet
if (0
< (inet |& getline))
print
}
print
"" |& inet
#
空行を送信
close(inet)
}
Lightweight
Language
のイベント、
じゃんけん対決の
プロトコル
(じゃんけん2.0)
のサンプル実装で、手を出して勝負をする対戦者
(エージェント)
と、対戦者の手を仲介するコーディネイタにより構成
される。
エージェント (対戦クライアント)
コーディネイタの指示に基づいて n
ラウンドの対戦をおこなう、エージェントの例を下記に示す。
make ja
1 3 ぐー ぐー 1
1 3 ちょき ぱー 2
BEGIN { # じゃんけん2.0
エージェント
inet = (1 < ARGC) ? ARGV[1] :
"/inet/tcp/0/localhost/12346"
RS = ORS = "\r\n"
JNAME[1] = "ぐー"; JNAME[2] = "ちょき";
JNAME[3] = "ぱー"
while(1) {
put("HELLO"); get()
if
("INITIATE" != $1) continue
# セッション開始
sid = $2
put("INITIATE " sid " awk4j 1"); get()
while
("READY" == $1) {
# ラウンド開始
round = $3
iteration = $4
SCORE[round] = 0
put("READY " sid " " round); get()
while (1) {
if
("CALL" == $1) {
# ここに思考ロジックを記述する
me = int(3 * rand()) + 1
put("MOVE " sid " " round " " me)
} else if
("RESULT" == $1) { # 相手の手を受信してスコアを表示
opp = $4
sc = opp - me; if (2 == sc) { sc = -1 }
else if (-2 == sc) { sc = 1 }
SCORE[round] += sc
print sid, round "\t" JNAME[me] "\t"
JNAME[opp] "\t" SCORE[round]
} else {
if ("MATCH" ==
$1)
# ラウンド終了
get()
break
}
get()
}
}
if
("CLOSE" == $1)
close(inet)
}
}
function get() {
if (0 > (inet
|& getline)) { print "ERROR:", ERRNO; exit(0) }
}
function put(s) {
print
s |& inet
}
コーディネイタ (仲介サーバ)
2つの対戦者(エージェント) と多重会話をおこなう例を下記に示す。
Note: マルチスレッドによる多重会話機能は、
AWK言語仕様の範囲内では実現不可能であるが、
AWK適用の可能性を探るために 試験的に実装し
た。
make j2
BEGIN { # じゃんけん2.0 コーディネイタ
inet = (1 < ARGC) ? ARGV[1] :
"/inet/tcp/12346/0/0"
RS = ORS = "\r\n"
JNAME[1] = "ぐー"; JNAME[2] = "ちょき";
JNAME[3] = "ぱー"
sessionID = 1; roundNo = 3; iteration =
100
B
= system("Barrier", 2) # スレド同期のためのバリアオブジェクトを作成
while(1) {
for (k in
MOVE) delete MOVE[k]
T[1] =
system("Thread", "accept", sessionID) #
指定した関数 accept()を新規スレッドで実行
T[2] =
system("Thread", "accept", sessionID+1)
system("join", T)
system("sleep 5")
}
}
function accept(sid,
round, iter)
{ #
この関数は多重スレッドで動作する
get(sid)
while(1) {
put("INITIATE " sid)
get(sid)
if
("HELLO" == S[sid, 1]) continue
if ("" ==
S[sid, 0]) { print ERRNO; return }
if
("INITIATE" == S[sid, 1]) {
# セッション開始
for (round = 1; round <= roundNo;
round++) {
SCORE[round] = 0
put("READY " sid " " round " " iteration " 1")
get(sid)
if
("READY" != S[sid, 1]) break
# ラウンド開始
for (iter
= 0; iter < iteration; iter++) {
put("CALL " sid " "
round) #
手の交換
get(sid)
if ("MOVE" != S[sid, 1]) break
MOVE[sid, round, iter] = S[sid, 4]
# while
(!((sid%2+1, round, iter) in MOVE)) system("sleep
0.001")
system("join",
B) #
このバリアで、双方の手が揃うのを待ち合わせる
put("RESULT " sid " " round " "
MOVE[sid%2+1, round, iter])
if (sid == sessionID) score(round, iter)
}
put("MATCH " sid " " round)
# ラウンド終了
}
break
} else {
get(sid)
}
}
put("CLOSE " sid)
close(inet)
}
function score(round, iter, m1, m2, sc) {
m1 = MOVE[1, round, iter]
m2 = MOVE[2, round, iter]
sc = m2 - m1; if (2 == sc) { sc = -1 }
else if (-2 == sc) { sc = 1 }
SCORE[round] += sc
print round, iter+1 "\t" JNAME[m1] "\t"
JNAME[m2] "\t" SCORE[round]
}
function get(sid, fn, T) {
inet
|& getline S[sid, 0]
fn = split(S[sid, 0], T) # $i
の代わりに S[sid, i]を使用する
for (; 0 < fn; fn--)
S[sid,
fn] = T[fn]
}
function put(s) {
print
s |& inet
}
Note: 実際に、じゃんけん対決を試してみる場合には、
じゃんけん大会の優
勝プログラム との対戦をお勧めする。