1

人々がログインし、いくつかのコマンドを送信/入力してログアウトするサーバーを作成したいと思います。多くの人が同時に接続している可能性がありますが、「名前を送信しています」、「パスワードを送信しています」、「アップロードコマンドの第2段階にある」など、各人の状態変数を多くしたくありません。 "...着信接続ごとにこのスクリプトの呼び出しを1回実行する方がはるかに簡単です。

puts -nonewline $out "login: "
gets $in login ;# check for EOF
puts -nonewline $out "password: "
gets $in password ;# check for EOF
while {[gets $in command] >= 0} {
  switch -- $command {
    ...
  }
}

約50の接続がある場合でも、接続ごとに1つのインタープリターを作成しても、メモリと速度は問題ありませんか?それとも、これはスレッドでできることですか?

4

1 に答える 1

2

少し実験して(システムツールを使用した対話型セッションを監視)、Tclアプリケーションプロセス内の各Tclインタープリターは、追加のユーザーコマンドなしで、300kBから350kBの間のどこかにかかることがわかります。スタックフレーム(インタープリターで何かを実行するために必要)と同様に、ユーザーコマンドとスクリプトはその上に追加されます。掛け算すると、50のインタプリタコンテキストでおそらく17MBが得られます。これは、最近のコンピュータであれば、ビートをスキップすることなく処理できます。ちなみに、インタプリタは同時実行を許可していません。

Tclのスレッドモデルには独自のマスターインタープリターを持つスレッドがあるため、スレッドの重みは大きくなります(実際、すべてのインタープリターは単一のスレッドに厳密にバインドされます。これは、Tclの実装でグローバルロックの量を大幅に削減するために使用される手法です)。このため、推奨されるスレッド数は、デプロイメントハードウェアで使用可能なCPUの数と、コードがIOバウンドではなくCPUバウンドである程度に大きく依存します。


Tcl 8.6を使用できる場合(8.6.0は、これを書いているときにリポジトリでリリース用にタグ付けされていますが、出荷されていません)、コルーチンを使用して接続状態をモデル化できます。それらはインタプリタよりもはるかに軽量であり、一種の協調マルチタスクを実行するために使用できます。

# Your code, with [co_gets] (defined below) instead of [gets]
proc interaction_body {in out} {
    try {
        puts -nonewline $out "login: "
        co_gets $in login ;# check for EOF
        puts -nonewline $out "password: "
        co_gets $in password ;# check for EOF
        if {![check_login $login $password]} {
            # Login failed; go away...
            return
        }
        while {[co_gets $in command] >= 0} {
          switch -- $command {
            ...
          }
        }
    } finally {
        close $in
    }
}

# A coroutine-aware [gets] equivalent. Doesn't handle the full [gets] syntax
# because I'm lazy and only wrote the critical bits.
proc co_gets {channel varName} {
    upvar 1 $varName var
    fileevent $channel readable [info coroutine]
    while 1 {
        set n [gets $channel var]
        if {$n >= 0 || ![fblocked $channel]} {
            fileevent $channel readable {}
            return $n
        }
        yield
    }
}
# Create the coroutine wrapper and set up the channels
proc interaction {sock addr port} {
    # Log connection by ${addr}:${port} here?
    fconfigure $sock -blocking 0 -buffering none
    coroutine interaction_$sock interaction_body $sock $sock
}

# Usual tricks for running a server in Tcl
socket -server interaction 12345;  # Hey, that port has the same number as my luggage!
vwait forever

これは、CPUを集中的に処理する必要があり、ログインの保護に注意する必要がある場合には適していません(SSLを使用した接続を保護するためにtlsパッケージを使用することを検討してください)。

于 2012-12-12T09:45:03.300 に答える