4

HTTP 1.1 を使用してサーバーで HTTP 要求を行い、接続を閉じない Java プログラムがあるとします。1 つの要求を作成し、ソケットにバインドした入力ストリームから返されたすべてのデータを読み取ります。ただし、2 番目の要求を行うと、サーバーから応答がありません (または、ストリームに問題があり、それ以上入力が提供されません)。リクエストを順番に(リクエスト、リクエスト、読み取り)行うと正常に機能しますが、(リクエスト、読み取り、リクエスト、読み取り)は機能しません。

なぜこれが起こっているのかについて、誰かが洞察を与えることができますか? (コード スニペットが続きます)。何をしても、2 番目の読み取りループの isr_reader.read() は -1 しか返しません。

try{
        connection = new Socket("SomeServer", port);
        con_out = connection.getOutputStream();
        con_in  = connection.getInputStream();
        PrintWriter out_writer = new PrintWriter(con_out, false);
        out_writer.print("GET http://somesite HTTP/1.1\r\n");
        out_writer.print("Host: thehost\r\n");
        //out_writer.print("Content-Length: 0\r\n");
        out_writer.print("\r\n");
        out_writer.flush();

        // If we were not interpreting this data as a character stream, we might need to adjust byte ordering here.
        InputStreamReader isr_reader = new InputStreamReader(con_in);
        char[] streamBuf = new char[8192];
        int amountRead;
        StringBuilder receivedData = new StringBuilder();
        while((amountRead = isr_reader.read(streamBuf)) > 0){
            receivedData.append(streamBuf, 0, amountRead);
        }

// Response is processed here.

        if(connection != null && !connection.isClosed()){
            //System.out.println("Connection Still Open...");

        out_writer.print("GET http://someSite2\r\n");
        out_writer.print("Host: somehost\r\n");
        out_writer.print("Connection: close\r\n");
        out_writer.print("\r\n");
        out_writer.flush();

        streamBuf = new char[8192];
        amountRead = 0;
        receivedData.setLength(0);
        while((amountRead = isr_reader.read(streamBuf)) > 0 || amountRead < 1){
            if (amountRead > 0)
                receivedData.append(streamBuf, 0, amountRead);
        }
}
        // Process response here
    }

質問への回答: はい、サーバーからチャンクされた回答を受信して​​います。外部の制限により、生のソケットを使用しています。

コードがごちゃごちゃしていて申し訳ありません - 私は記憶から書き直していたので、いくつかのバグを導入したようです。

したがって、コンセンサスは、(要求、要求、読み取り) を実行して、最後に到達したらサーバーにストリームを閉じさせるか、または (要求、読み取り、要求、読み取り) を実行した場合、ストリームの最後に到達する前に停止する必要があるということです。ストリームが閉じないようにします。

4

5 に答える 5

5

コードによると、2番目のリクエストの送信を処理するステートメントに到達するのは、サーバーが最初のリクエストを受信/応答した後に出力ストリーム(入力ストリーム)を閉じるときだけです。

その理由は、最初の応答のみを読み取ることになっているコードであるためです

while((amountRead = isr_reader.read(streamBuf)) > 0) {
  receivedData.append(streamBuf, 0, amountRead);
}

サーバーが出力ストリームを閉じるまで(つまり、readreturnsのとき-1)、またはソケットの読み取りタイムアウトが経過するまでブロックします。読み取りタイムアウトの場合、例外がスローされ、2番目の要求を送信することさえできません。

HTTP応答の問題は、応答が終了するまでストリームから読み取るバイト数が通知されないことです。これはHTTP1.0応答にとっては大したことではありません。サーバーは応答後に接続を閉じるだけなので、ストリームの最後まですべてを読み取るだけで応答(ステータス行+ヘッダー+本文)を取得できます。

HTTP 1.1持続的接続を使用すると、ストリームが終了するまですべてを単純に読み取ることができなくなります。最初にステータス行とヘッダーを1行ずつ読み取る必要があります。次に、ステータスコードとヘッダー(Content-Lengthなど)に基づいて、応答本文を取得するために読み取るバイト数を決定します(すべて)。上記を適切に行うと、接続が閉じられるかタイムアウトが発生する前に読み取り操作が完了し、サーバーが送信した応答を正確に読み取ることができます。これにより、次のリクエストを送信してから、最初のリクエストとまったく同じ方法で2番目のレスポンスを読み取ることができます。

PSリクエスト、リクエスト、読み取りは、サーバーがリクエストパイプラインをサポートし、両方のリクエストを受信して​​処理するという意味で「機能」している可能性があります。その結果、両方のレスポンスを「最初の」レスポンスとして1つのバッファに読み込みます。

PPSエンコーディングPrintWriterを使用していることを確認してください。US-ASCIIそうしないと、システムエンコーディングによっては、HTTPリクエストのリクエストラインとヘッダーの形式が正しくない(エンコーディングが間違っている)可能性があります。

于 2008-10-08T23:04:29.683 に答える
3

RFC に準拠した単純な http/1.1 クライアントを作成することは、それほど難しい作業ではありません。Java でソケットを読み取るブロック入出力アクセスの問題を解決するには、java.nio クラスを使用する必要があります。SocketChannel は、ノンブロッキング I/O アクセスを実行する可能性を提供します。

これは、永続的な接続で HTTP 要求を送信するために必要です。

さらに、nio クラスはパフォーマンスが向上します。

私のストレステストでは、次の結果が得られます。

  • HTTP/1.0 (java.io) -> HTTP/1.0 (java.nio) = +20% 高速化

  • HTTP/1.0 (java.io) -> HTTP/1.1 (固定接続の java.nio) = +110% 高速

于 2010-07-21T15:29:17.243 に答える
0

Connection: keep-aliveリクエストにが含まれていることを確認してください。ただし、これは重要なポイントかもしれません。

サーバーはどのような応答を返しますか?チャンク転送を使用していますか?サーバーが応答本文のサイズを認識していない場合、サーバーはContent-Lengthヘッダーを提供できず、コンテンツが終了したことをクライアントに示すために、応答本文の最後で接続を閉じる必要があります。この場合、キープアライブは機能しません。PHPやJSPなどを使用してオンザフライでコンテンツを生成している場合は、出力バッファリングを有効にし、蓄積された本文のサイズを確認し、Content-Lengthヘッダーをプッシュして出力バッファをフラッシュできます。

于 2008-10-08T15:45:43.860 に答える
0

JavaのURL接続やCommonsHTTPClientではなくrawソケットを使用している特別な理由はありますか?

HTTPを正しく理解するのは簡単ではありません。Commons HTTP Clientは、あなたがやろうとしているように接続を再利用できることを私は知っています。

ソケットを使用する特別な理由がない場合は、これがお勧めです:)

于 2008-10-08T15:55:05.620 に答える
0

独自の正しいクライアントHTTP/1.1実装を作成することは簡単ではありません。歴史的に、私が見たほとんどの人はそれを試みましたが、それは間違っていました。それらの実装は通常、仕様を無視し、特定の1つのテストサーバーで機能するように見えることを実行します。特に、チャンク化された応答を処理できるという要件を通常は無視します。

非常に奇妙な要件がない限り、独自のHTTPクライアントを作成することはおそらく悪い考えです。

于 2008-10-12T08:21:01.373 に答える