Internetworking

awk4j (AWK for Java Platform)

Internetworking とは

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

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")
    }
}

http (HyperText Transfer Protocol)

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)
    }
}

echo

サーバの多重スレッド化

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)
}

じゃんけん2.0

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: 実際に、じゃんけん対決を試してみる場合には、 じゃんけん大会の優 勝プログラム との対戦をお勧めする。

AWK 関連リンク


Internetworking
  1. daytime
    1. 公開NTPサーバにアクセス
    2. UDPディレクティッド・ブロードキャストで送信
  2. http (HyperText Transfer Protocol)
    1. 検索エンジンにアクセス
    2. httpサーバ
  3. echo
    1. サーバの多重スレッド化
  4. じゃんけん2.0
    1. エージェント (対戦クライアント)
    2. コーディネイタ (仲介サーバ)