20

Java でソケットを使用する場合、処理を開始する前に、クライアントがすべての (バイナリ) データの送信を終了したかどうかをどのように確認できますか。たとえば、次のように考えてください。

istream = new BufferedInputStream (socket.getInputStream());
ostream = new BufferedOutputStream(socket.getOutputStream());

byte[] buffer = new byte[BUFFER_SIZE];

int count;
while(istream.available() > 0 && (count = istream.read(buffer)) != -1)
{
    // do something..
}

// assuming all input has been read
ostream.write(getResponse());       
ostream.flush();

thisなどのSOに関する同様の投稿を読みましたが、決定的な答えが見つかりませんでした。上記のソリューションは機能しますが、クライアントがすべてのデータの送信を終了したかどうかを実際に判断することはできないと理解しています。たとえば、クライアント ソケットがいくつかのデータ チャンクを送信し、別のデータ ソースからのデータを待機してからさらにデータを送信できるようにブロックする場合、上記のコードは、クライアントが istream.available() 以降のすべてのデータの送信を完了したと想定する可能性があります。現在のバイト ストリームに対して 0 を返します。

4

4 に答える 4

26

はい、その通りですavailable()。このように使用するのは信頼できません。個人的に私はめったに使用しませんavailable()ストリームの最後に到達するまで(質問のタイトルに従って)読みたい場合は、read()-1 が返されるまで呼び出しを続けます。それは簡単なビットです。難しいのは、ストリームの終わりではなく、「サーバーが現在送信したいもの」の終わりが必要な場合です。

他の人が言ったように、ソケットを介して会話する必要がある場合は、データがどこで終了するかをプロトコルに説明させる必要があります。個人的には、可能な場合は「メッセージトークンの終わり」ソリューションよりも「長さプレフィックス」ソリューションを好みます。一般に、コードの読み取りがはるかに簡単になります。ただし、何かを送信する前に長さを計算する必要があるため、コードの記述が難しくなる可能性があります。大量のデータを送信できる場合、これは苦痛です。

もちろん、ソリューションを組み合わせて使用​​することもできます。特に、プロトコルがテキスト データとバイナリ データの両方を扱う場合は、null で終わる文字列 (または類似のもの) ではなく、長さのプレフィックス文字列を使用することを強くお勧めします。文字列データのデコードは、デコーダーに完全なバイト配列を渡して文字列を取得するだけで、はるかに簡単になる傾向があります。たとえば、文字の途中まで読み取ることを心配する必要はありません。これをプロトコルの一部として使用できますが、リーダーがデータを処理して応答できるようにするための「データの終わり」レコードを含む全体的な「レコード」(または送信しているもの) がまだあります。

もちろん、プロトコルを制御していない場合、このプロトコル設計のすべては意味がありません:(

于 2009-03-16T06:28:07.970 に答える
7

あなたがアプリケーションの送信側と受信側の両方を作成する人であると仮定すると、これはよりプロトコル的なタスクだと思います。たとえば、単純なロジック プロトコルを実装して、データをパケットに分割できます。次に、パケットを頭と体の 2 つの部分に分けます。そして、頭が事前定義された開始シーケンスで構成され、本体にバイト数が含まれていると言います。パケットの最初のバイトとして、bofy の開始シーケンスと単純な転送バイト数を忘れてください。その後、問題を解決できます。

于 2009-03-16T05:15:19.427 に答える
4

一部のpplがすでに言ったように、通信のためにある種のプロトコルを避けることはできません。たとえば、次のようになります。

サーバー側には次のものがあります。

 void sendMSG(PrintWriter out){
    try {
        //just for example..
        Process p = Runtime.getRuntime().exec("cmd /c dir C:");
        BufferedReader br = new BufferedReader(new InputStreamReader(
            p.getInputStream()));

        //and then send all this crap to the client
        String s = "";
        while ((s = br.readLine()) != null) {
          out.println("MSG");
          out.println(s);
        }
      } catch (Exception e) {
        System.out.println("Command incorrect!");
      }
      out.println("END");
}
//You are not supposed to close the stream or the socket, because you might want to send smth else later..

クライアント側には次のものがあります。

void recieveMSG(BufferedReader in) {
    try {
      while (in.readLine().equals("MSG")) {
        System.out.println(in.readLine());
      }
    } catch (IOException e) {
      System.out.println("Connection closed!");
    }
  }
于 2011-06-09T08:48:09.647 に答える
2

ニキータが言ったように、これはよりプロトコルのタスクです。ヘッダーと本文のアプローチを使用するか、ストリームの終わりに特殊文字または記号を送信して処理ループを中断することができます。ストリームの終わりを示すためにソケットで '[[END]]' と言うようなものです。

于 2009-03-16T05:21:12.860 に答える