java.net.SocketException:壊れたパイプを修正する方法


150

私はapache commons http clientを使用して、postメソッドを使用してurlを呼び出し、パラメーターを送信していますが、まれに以下のエラーがスローされます。

java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
        at java.io.BufferedOutputStream.write(BufferedOutputStream.java:105)
        at java.io.FilterOutputStream.write(FilterOutputStream.java:80)
        at org.apache.commons.httpclient.methods.ByteArrayRequestEntity.writeRequest(ByteArrayRequestEntity.java:90)
        at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:499)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)

誰かがこの例外の原因とそれをデバッグする方法を提案できますか?


回答:


81

これは次の原因で発生します:

  • ほとんどの場合、もう一方の端がすでに接続を閉じているときに接続に書き込みます。
  • あまり一般的ではありませんが、ピアは自分の側ですでに保留中のすべてのデータを読み取らずに接続を閉じます。

したがって、どちらの場合でも、アプリケーションプロトコルの定義や実装が不十分です。

ここでは説明しませんが、ピアが接続を適切に閉じるのではなく、意図的にリセットするために意図的なアクションをとる3番目の理由があります。


4
それを特定して修正するのを手伝ってくれませんか?基本的に、理由を確認して修正するにはどうすればよいですか?

4
私がしている理由を確認しました。もう一方の端がすでに接続を閉じている間、あなたは書いています。修正はそれをしないことです。もう一方の端はそれを読んでいないので、とにかく何の意味もありません。私が言ったように、これが発生している場合、アプリケーションプロトコルの仕様または実装に問題がある可能性があります。
ローン侯爵

26
サーバー側のHTTPアプリケーションでブロークンパイプの例外が発生している場合は、クライアントブラウザーが終了したか、別のページに移動したか、タイムアウトになったか、履歴に戻ったかなどです。忘れてしまいなさい。
ローン侯爵

3
ブラウザでajaxリクエストを中止するときに(を使用してXMLHttpRequest.abort())この例外を見てきました。
オベッカー2013

2
考えられる原因の1つは、プロキシまたはHttpサーバーが接続を早く閉じすぎていることです。たとえば、J2EEサーバーとアプリケーションクライアント間のApache Httpサーバーで、タイムアウトが短く定義されています。少なくとも、それが本番環境で起こったことです。JBossのログでこのエラーが発生したため、ページが完全にロードされていないというクライアントの苦情がありました。いくつかのテストの結果、問題の構成が適切でないHttpサーバーであることがわかりました。
Ricardo Vila

32

私たちのケースでは、アプリサーバーで負荷テストを実行しているときにこれを経験しました。この問題は、JVMが不足していたため、JVMにメモリを追加する必要があることがわかりました。これで問題は解決しました。

これらのエラーが発生した場合は、JVMで使用可能なメモリを増やすか、メモリ使用量を監視してください。


4
負荷テスト中に同じ問題が発生しました。データの生成にはかなりの時間がかかり、負荷テストツールはデータを十分に長く待たないため、接続を閉じます。私の場合、メモリを追加するとデータ生成プロセスの時間が短縮され、負荷テストでタイムアウトの制限なしにすべてのデータが取得された可能性があります。メモリを増やす別の方法は、負荷テストツールのタイムアウトを増やすことでした。
Julien Kronegg 2013年

2
明らかに、メモリ不足のために、アプリケーションは受信ソケットを閉じるか、実際には完全に終了し、同じ効果がありました。メモリ不足原因でパイプが壊れることはありません。
ローンの侯爵2015年

1
これは、実際に私たちの高負荷アプリケーションの間の問題でもあるようです
Ruben de Vries

7

SocketException:パイプの破損は、コードが接続の読み取りまたは書き込みを行っている間に、「もう一方の端」(クライアントまたはサーバー)が接続を閉じたことが原因で発生します。

これは、アプリケーション制御外のクライアントまたはサーバーからトラフィックを受信するクライアント/サーバーアプリケーションで非常に一般的な例外です。たとえば、クライアントはブラウザです。ブラウザーがAjax呼び出しを行うか、ユーザーがページまたはブラウザーを閉じるだけの場合、これによりすべての通信が予期せず効果的に終了する可能性があります。基本的には、もう一方の端がアプリケーションを終了するときにこのエラーが表示され、予期していませんでした。

アプリケーションでこの例外が発生した場合は、IO(入力/出力)が発生するコードを確認し、それをtry / catchブロックでラップして、このIOExceptionをキャッチする必要があります。その場合、この準有効な状況をどのように処理するかを決定するのはあなたです。

あなたの場合、あなたがまだ制御できる最も早い場所はHttpMethodDirector.executeWithRetry呼び出しです-したがって、呼び出しがtry / catchブロックでラップされていることを確認し、あなたが適切に見えるように処理してください。

デバッグ/トレースレベル以外では、SocketException-Broken Pipe固有のエラーをログに記録しないことを強くお勧めします。そうでない場合、これはログを埋めることにより、DOS(サービス拒否)攻撃の一形態として使用できます。この一般的なシナリオについて、アプリケーションを強化してネガティブテストを行ってください。


ピアが接続を読み取っているときに接続を閉じたことが原因ではありません。これにより、別のストリーム終了条件が発生しますが、これは多くの場合例外ではありません。
ローンの侯爵

5

開いているすべてのストリームと接続を適切に閉じる必要があるため、次にurlConnectionオブジェクトを使用しようとしたときにエラーはスローされません。例として、次のコード変更によりエラーが修正されました。

前:

OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Some text");
bw.close();
out.close();

後:

OutputStream os = urlConnection.getOutputStream();
OutputStream out = new BufferedOutputStream(os);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
bw.write("Some text");
bw.close();
out.close();
os.close(); // This is a must.

8
これは実際には非常に奇妙です。なぜなら、はBufferedOutputStream常にclose()基礎となる出力ストリーム(したがって、の出力ストリームurlConnection)を呼び出すからです。docs.oracle.com/javase/6/docs/api/java/io/…docs.oracle.com/javase/6/docs/api/java/io/…を参照してください。呼び出すときは、os.close()すでに閉じられているはずです。。番号?
オベッカー2013

4
「os.close()」は「必須」ではありません。どちらも 'out.close()'の問題ではありません。'bw.close()'で十分です。@obedkerは正しいです。この変更だけでは問題を解決できませんでした。
ローンの侯爵

1

FTPサーバーを介したデータダウンロード機能を実装しましたが、ダウンロードの再開中に同じ例外が見つかりました。この例外を解決するには、常に前のセッションから切断し、クライアントの新しいインスタンスとサーバーとの新しい接続を作成する必要があります。これと同じアプローチは、HTTPClientにも役立ちます。


このエラーを解決するには、閉じたソケットを使用しないようにする必要があります。
ローン侯爵

3
笑。SOに関するすべての偽のアドバイスを修正して、一日中無駄にすることができます。
デイブ

2
@Dave確かに。たまにそうします。
ローン侯爵

1

特定のTCPをリッスンする単純なJavaアプリケーションを開発しているときにも、同じ問題が発生します。通常は問題ありませんでしたが、ストレステストを実行したところ、一部の接続でエラーが発生しました。socket write exception

調査の後、私は自分の問題を解決する解決策を見つけました。私はこの質問がかなり古いことを知っていますが、私は私の解決策を共有することを好みます、誰かがそれを役に立つと感じることができます。

問題は、ServerSocketの作成でした。私はJavadocを読んでいますが、デフォルトの制限は50の保留ソケットです。別の接続を開こうとすると、これらは拒否されます。ソリューションは、サーバー側でこのデフォルト構成を変更するだけです。次のケースでは、TCPポートでリッスンし、保留中の10_000最大200ソケットを受け入れるソケットサーバーを作成します。

new Thread(() -> {
      try (ServerSocket serverSocket = new ServerSocket(10_000, 200)) {
        logger.info("Server starts listening on TCP port {}", port);

        while (true) {
          try {
            ClientHandler clientHandler = clientHandlerProvider.getObject(serverSocket.accept(), this);
            executor.execute(clientHandler::start);
          } catch (Exception e) {
            logger.error(e.getMessage());
          }
        }

      } catch (IOException | SecurityException | IllegalArgumentException e) {
        logger.error("Could not open server on TCP port {}. Reason: {}", port, e.getMessage());
      }
    }).start();

ServerSocketの Javadocから:

着信接続表示(接続要求)の最大キュー長は、バックログパラメータに設定されます。キューがいっぱいのときに接続指示が到着すると、接続は拒否されます。


0

問題は、デプロイされたファイルが正しいRMIメソッドで更新されていない可能性があります。RMIインターフェイスに更新されたパラメーター、またはクライアントにはない更新されたデータ構造があることを確認してください。または、RMIクライアントに、サーバーのバージョンとは異なるパラメーターがないこと。

これは単なる知識に基づく推測です。サーバーアプリケーションのクラスファイルを再デプロイして再テストした後、「壊れたパイプ」の問題はなくなりました。


どの RMIメソッド?RMIは質問で言及されていません。
ローン侯爵

0

上記の回答は、この理由を示してjava.net.SocketException: Broken pipeいます。もう一方の端が接続を閉じました。私がそれに遭遇したときに何が起こったかの経験を共有したいと思います:

  1. クライアントのリクエストでは、 Content-Typeヘッダーがリクエスト本文よりも実際に大きく設定されている(実際には本文がまったくなかった)
  2. Tomcatソケットの一番下のサービスはそのサイズのボディデータを待っていました(httpはTCP上にあり、カプセル化することで配信を保証します...)
  3. 60秒が経過すると、tomcatはタイムアウト例外をスローします。 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. タイムアウト例外のため、クライアントはステータスコード500の応答を受信します。
  5. クライアントが接続を閉じる(応答を受信するため)。
  6. java.net.SocketException: Broken pipeクライアントが閉じたため、tomcatがスローします。

ときどき、タイムアウト例外が接続を閉じるため、Tomcatが壊れたPIP例外をスローしないことがあります。そのような違いが私を混乱させる理由です。


Content-Typeそれは何よりも「大きな」にすることはできませんので、ヘッダには、番号を指定しません。タイムアウト例外はソケットを閉じません。答えはまったく意味がありません。
ローン侯爵

@EJP多分それはコンテンツの長さだった、覚えていない。Tomcatのタイムアウトにより500が返されると思いますが、クライアントはこの応答を受信して​​接続を閉じます。これにより、最終的にTomcatは期待どおりの十分なバイトを受信しなくなります。
Tiina

Tomcatが500を送信する場合、計画しているすべてをすでに受け取っています。まだ意味がありません。
ローンの侯爵

@ user207421本当にそうではありません。Tomcatは期待するすべてを受信しないため、500を送信しますが、タイムアウトしました。
Tiina、

-1

JavaDoc:

着信接続指示(接続要求)の最大キュー長は50に設定されています。キューがいっぱいのときに接続指示が到着すると、接続は拒否されます。

たとえば、ServerSocketの「バックログ」パラメータを増やす必要があります。

int backlogSize = 50 ;
new ServerSocket(port, backlogSize);

1
バックログを増やしても、「パイプの破損」エラーは解決されません。
ローンの侯爵

1
しかし、それは私のために役立った
TheSecond '25

@EJP私はLinuxでサーバーをテストしましたが、動作しましたが、Mac OSでは動作しませんでした。そして、バックログのサイズを大きくすることが私に役立ちました。最大サイズが50であることを知りませんでした。
TheSecond

1
あなたは何か他のものをテストしました。リッスンバックログは、この例外とは関係ありません。クライアントでの接続拒否またはタイムアウトに役立ちます。ローカルエラーである「ソケットクローズ」例外ではありません。
ローン侯爵

ソケットプール(TomcatのようなHTTPサーバーなど)の場合、サービススレッドをディスパッチする前にサーバーが「キューに入れる」保留中の「受け入れ」の数の設定と、プール内のスレッド数の個別の設定があります。しかし、これのどれもがその問題に関連されていない後に接続が確立されると、サーバー「受け入れる()」 -出力ストリームは、(無意識のうちに)「閉」突然です。
ダレルティーグ

-1

原因は、リモートピアがソケットを閉じる(クラッシュなど)ためです。たとえば、リモートピアにデータを書き込もうとすると、これが発生します。それを解決するには、(catchを試行する)間のソケット操作の読み取り/書き込みを保護し、接続が失われた場合を管理してcatch(接続の更新、プログラムの停止など)に入れます。

 try {
      /* your code */
 } catch (SocketException e) {
      e.printStackTrace();
      /* insert your failure processings here */
 }

3
これは問題ログに記録しますが解決しません。それらを解決するには、(a)考えられるアプリケーションプロトコルエラーを解決し、(b)デッド接続を閉じる必要があります。例外をログに記録するだけでなく、そのほとんどは致命的です。
ローン侯爵

@EJP:もちろん、エラーをキャッチするときは、catchステートメントにソケット(コンテキスト)をクリーンアップする必要があります。開発者が何をしようとしているのかはわかりません。クリーンな手順を実行するのは彼自身です
elhadi dpıpɐɥןǝ18/

エラーの原因となっているアプリケーションプロトコルの問題を修正するのは開発者の責任であり、あります。彼に役立つあなたの答えやコードは何もありません。そうする。「間に読み取り/書き込み操作を挿入」はそれを修正しません。あなたの答えは間違っています。
ローン侯爵

@ user207421、開発者は壊れたパイプを修正する方法を尋ねます。私は(キャッチを試す)を使用して最初にプログラムを保護するように彼に指示しました。これにより、プログラムのクラッシュを回避できます。次に、コメントを挿入して、クリーンな処理を行うように彼に指示します。一般的に、壊れたパイプのエラーでは、多くの選択肢があります。プログラムの目的を推測できない、プログラムを停止する、接続を更新する、データが破損しているかどうかなどです。実行するオプションを決定するのはプログラマの責任ですか?私の答えは何が悪かったのですか?
elhadi dpıpɐɥןǝ19年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.