web-dev-qa-db-ja.com

データをncにパイプする際の問題

PHPで、ソケットをリッスンし、コマンドを受け入れ、アクションを実行する小さなUDPサーバーを作成しました。これは非常に基本的なことです。次のように手動で接続できます。

% nc -u Host port

(ここで、nc = Ncat:バージョン7.50( https://nmap.org/ncat ))

コマンドを入力すると、結果の応答が表示されます。それは私がコマンドラインから望むように正確に機能します。

ただし、次のようにファイルを単に「cat」すると、次のようになります。

cat FILE| nc -u Host port

または、次のようなデータを送信します。

echo "command1\ncommand2\n" | nc -u Host port

...次に、私のPHPアプリは、行末文字を含むすべてを一度に読み取ります。行末まで読み取りたいだけです。

確かに、ファイルの内容をラップアラウンドして、各行をncに送信できます。

for x in `cat <file>`; do
  echo $x | nc -u Host port
done

...しかし、それは完全な無駄です。 ncへの接続が1つ必要ですが、多くはありません。

PHP appで出力を出力すると、command1 command2 ...と表示されますが、すべて1つの文字列になっているため、EOL文字が文字列に組み込まれます。

インタラクティブモードの動作が非インタラクティブモードと異なるのはなぜですか?

私は午後中ずっと実験をしてきました、そして私はこの仕事をすることができないようです。

でも説明はあると思います。

あなたが提供できるどんな情報にも感謝します。

PS:PHPコードの基本は:

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock, '0.0.0.0', 2000);
for (;;) {
  socket_recvfrom($sock, $cmd, 1024, 0, $ip, $port);
  echo "command is $cmd";
  $reply = process($cmd);
  echo "reply is $reply";
  socket_sendto($sock, $reply, strlen($reply), 0, $ip, $port);
}

Ncでインタラクティブにテキストを入力しているときに、CTRL-Dを押してパケットを分割できます。シェルスクリプトで同じことを行う場合:

printf 'command1\0014commandd2\0014'| nc -u Host port

...コマンドはすべて1つのコマンドとして表示されます。

PHPコード、たとえば5でコードのパケットサイズを小さくすると、20バイトの長さのデータが送信され、データは切り捨てられ、分離されません。

私もオンラインで準備ができています。これはバッファリングの問題である可能性があります。私は使ってみました:

stdbuf -oL -eL cat FILE | nc -u Host port 

...しかし、驚くべきことに、それも違いを生みませんでした。

最後に、私がそうするならば、私はそれを発見しました:

for x in command1 command2; do 
  echo $x 
  sleep 1
done | nc -u Host port

...すべてが計画どおりに進むこと。サーバーは最初のコマンドを受信し、次に2番目のコマンドを受信します。

はっきりしないのは、なぜ睡眠1が違いを生むのかということです。取り出して失敗します。上記は、各エコーをncに送信するよりも確かに優れています。

2
Jason K

あなたのコードはほとんどそこにあります、今あなたがする必要があるすべては$cmdを改行で終わる文字列のキューとして扱うことです。ナイーブなPython風の擬似コードでは、PHPを長い間記述していないため:

input = ''
try:
    first_command, remaining_input = input.split('\n', 1)
    execute(first_command)
    input = remaining_input
except ValueError:
    # No newline yet; we have to wait for more input
    input = socket.receive()

このようにして、入力ストリームと単一の対話型コマンドの両方を処理できますが、これはおそらくアプリケーションの使用方法ではありません。

0
l0b0

l0b0はあなたの問題を解決しましたが、他のいくつかの点に答えるために:

確かに、ファイルの内容をラップして、各行をnc:for x in $(cat <file>); do echo $x | nc -u Host port; doneに送信することはできますが、それは完全に無駄です。 ncへの接続が1つ必要ですが、多くはありません。

1)実際にはファイルから行を送信せず、空白で区切られたものを送信します。空白は改行だけでなく、タブまたはスペースでもかまいません(IFSを変更していない場合)。 「コマンド」のアイデアが単一の単語に制限されている場合にのみ、これらは同じですが、一般的にほとんどのUnix(y)コマンドは複数の単語です。これらの単語はシェルパス名拡張(「グロビング」と呼ばれることも多い)のために処理されるため、?*[..]のいずれかが含まれている場合、$xに使用される値はファイル内の値と異なる場合があります。

2)UDPはコネクションレスであり、接続さえ存在しないため、接続は「無駄」になりません。たぶん、あなたはncプログラムの実行を(ローカルで)望まないということです。

インタラクティブモードの動作が非インタラクティブモードと異なるのはなぜですか?

Unixのターミナルでの入力は、通常、一度に1行ずつ処理されます。プログラム(ここではnc)がreadを実行すると、return(または同等の文字)を押すまで、必要な数の文字を入力し、オプションで編集するまで待機します。入力行がプログラムに配信されます(ncの場合はパケットとして送信されます)。 (プログラムのバッファーが小さすぎる場合を除き、収まる部分のみが配信され、残りは次の読み取りまで保留されます。)オプションで、これをオフにして、各文字(または1回のキーストロークで生成される文字のグループ)と同じくらい少なくすることができます。ファンクションキーなど)は個別に配信できます。これは、vi-や、あまり明確ではありませんが、bashなどのプログラムで使用されます。正式にはこれらは 「カノニカル」および「非カノニカル」モード と呼ばれますが、歴史的に非カノニカルモードは「生」、カノニカルモードは「調理済み」と呼ばれていました。

パイプを含む他のタイプのファイルからの読み取りは、改行(つまり改行文字)を無視し、バッファー(この場合は複数行を含む)に収まる限り読み取ります。

Ncでインタラクティブにテキストを入力しているときに、CTRL-Dを押してパケットを分割できます。シェルスクリプトで同じことを行う場合:printf 'command1\0014commandd2\0014'| nc -u Host port ...コマンドはすべて1つのコマンドとして表示されます。

3)端末のcontrol-Dは特別です-より正確には、通常はcontrol-Dですが変更可能な端末ドライバのeof設定として構成された文字が特別です。プログラムの読み取り中にthis文字を入力すると、戻りを待たずに、またデータにcontrol-Dを含めずに、読み取りが終了します。ファイルで発生する場合、control-Dに対応する文字コードは特別ではありません。

4)そして、あなたが生成したキャラクターはとにかくコントロール-Dではありません。 Control-Dは、多くのバージョンのechoで使用される8進表記では\004であり、他のいくつかのコンテキストでは\x04です。 \001はcontrol-Aであり、STXとも呼ばれ、Unixでは実際には使用されていません(複数の仮想端末ウィンドウ間の切り替えを制御するために使用するプログラムscreenを除く)。

私もオンラインで準備ができています。これはバッファリングの問題である可能性があります。 stdbuf -oL -eL cat FILE | nc -u Host port ...を使ってみましたが、意外にも違いはありませんでした。

5)stdbufは、Cライブラリの「stdio」ルーチンにのみ影響し、catは、テキストと非テキストの「バイナリ」の両方で機能するように設計されているため、おそらく使用しません。データ。私の最も便利なテストシステム(CentOS6)では、straceはGNU-coreutils-8.4catstdbufの影響を受けないことを確認していますが、他の実装が可能であることを否定することはできませんあります。 sed '' file; grep '' file; awk 1 fileのような行のテキストを処理するように設計されたプログラムを使用すると、stdbuf -oLが違いを生むことを確認します。

しかし、それはあなたが望む違いを生まないかもしれません。パイプに行を追加するライタープログラムとそれらを読み取る(および送信する)ncの間には競合状態があり、ライターの方が速い場合でも、複数の行が1つの発信パケットにまとめられます。

最後に、私は次のことを発見しました:for x in command1 command2; do echo $x; sleep 1; done | nc -u Host portすべてが計画どおりに進むこと。サーバーは最初のコマンドを受信し、次に2番目のコマンドを受信します。

はっきりしないのは、なぜ睡眠1が違いを生むのかということです。 ...

6)これにより競合状態が回避されます。シェルがパイプに1行を書き込むたびに、ncは、シェルの次の書き込みの前に、それを読み取る(そして送信する)時間が確実にあります。

1