Bagaimana cara memperbaiki java.net.SocketException: Pipa rusak?


150

Saya menggunakan klien http apache commons commons untuk memanggil url menggunakan metode post untuk memposting parameter dan jarang sekali melemparkan kesalahan di bawah ini.

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)

Adakah yang bisa menyarankan apa yang menyebabkan Pengecualian ini dan bagaimana cara debug itu?


Jawaban:


81

Ini disebabkan oleh:

  • paling sering, menulis ke koneksi ketika ujung yang lain sudah menutupnya;
  • Lebih jarang, peer menutup koneksi tanpa membaca semua data yang sudah tertunda.

Jadi dalam kedua kasus Anda memiliki protokol aplikasi yang didefinisikan atau diimplementasikan dengan buruk.

Ada alasan ketiga yang tidak akan saya dokumentasikan di sini tetapi yang melibatkan rekan kerja mengambil tindakan yang disengaja untuk mengatur ulang daripada menutup koneksi dengan benar.


4
Bisakah Anda membantu saya mengidentifikasi dan memperbaikinya? Pada dasarnya, bagaimana cara mengkonfirmasi alasan dan memperbaikinya?

4
Saya sudah mengkonfirmasi alasannya. Anda menulis sementara ujung lainnya sudah menutup koneksi. Cara mengatasinya bukan untuk melakukan itu. Karena ujung yang lain tidak membacanya, toh tidak ada gunanya. Seperti yang saya katakan, jika ini terjadi ada sesuatu yang salah dengan spesifikasi atau implementasi protokol aplikasi Anda, kemungkinan besar Anda bahkan tidak memilikinya.
Marquis of Lorne

26
Jika aplikasi HTTP sisi server mendapatkan pengecualian Pipa Rusak itu hanya berarti browser klien telah keluar / pergi ke halaman lain / habis waktu / kembali dalam sejarah / apa pun. Lupakanlah.
Marquis of Lorne

3
Kami telah melihat pengecualian ini ketika membatalkan permintaan ajax di browser (menggunakan XMLHttpRequest.abort()).
obecker

2
Salah satu kemungkinan penyebabnya adalah server proxy atau Http yang menutup koneksi terlalu dini. Misalnya server Apache Http antara server J2EE Anda dan klien aplikasi, dengan batas waktu singkat yang ditentukan. Setidaknya itulah yang terjadi pada lingkungan produksi kami. Keluhan klien tentang halaman tidak dimuat sepenuhnya dan kami melihat kesalahan ini pada JBoss log. Setelah beberapa pengujian kami perhatikan bahwa masalahnya adalah Http Server yang tidak terkonfigurasi dengan baik.
Ricardo Vila

32

Dalam kasus kami, kami mengalami hal ini saat melakukan uji beban di server aplikasi kami. Masalahnya ternyata kita perlu menambahkan memori tambahan ke JVM kita karena sudah habis. Ini menyelesaikan masalah.

Coba tingkatkan memori yang tersedia untuk JVM dan atau pantau penggunaan memori saat Anda mendapatkan kesalahan itu.


4
Saya mendapat masalah yang sama selama tes beban. Saya kira data membutuhkan banyak waktu untuk dihasilkan dan alat pengujian beban tidak menunggu data cukup lama kemudian menutup koneksi. Menurut saya, menambahkan memori mungkin telah mengurangi durasi proses pembuatan data sehingga uji beban mendapatkan semua data tanpa batas waktu habis. Alternatif untuk menambah memori adalah dengan menambah batas waktu alat uji beban.
Julien Kronegg

2
Jelasnya, memori yang rendah menyebabkan aplikasi menutup soket penerima atau bahkan keluar sama sekali, dengan efek yang sama. Memori rendah dengan sendirinya tidak menyebabkan pipa rusak.
Marquis of Lorne

1
ini tampaknya juga menjadi masalah selama aplikasi berat kita
Ruben de Vries

7

SocketException: Pipa rusak, disebabkan oleh 'ujung lain' (klien atau server) menutup koneksi saat kode Anda sedang membaca dari atau menulis ke koneksi.

Ini adalah pengecualian yang sangat umum dalam aplikasi klien / server yang menerima lalu lintas dari klien atau server di luar kendali aplikasi. Sebagai contoh, klien adalah browser. Jika browser membuat panggilan Ajax, dan / atau pengguna hanya menutup halaman atau browser, maka ini dapat secara efektif membunuh semua komunikasi secara tak terduga. Pada dasarnya, Anda akan melihat kesalahan ini kapan saja ujung yang lain menghentikan aplikasi mereka, dan Anda tidak mengantisipasinya.

Jika Anda mengalami Pengecualian ini di aplikasi Anda, maka itu berarti Anda harus memeriksa kode Anda di mana IO (Input / Output) terjadi dan membungkusnya dengan blok coba / tangkap untuk menangkap IOException ini. Maka, terserah Anda untuk memutuskan bagaimana Anda ingin menangani situasi semi-valid ini.

Dalam kasus Anda, tempat paling awal di mana Anda masih memiliki kontrol adalah ajakan untuk HttpMethodDirector.executeWithRetry- Jadi pastikan panggilan tersebut dibungkus dengan blok coba / tangkap, dan tangani sesuai keinginan Anda.

Saya akan sangat menyarankan agar logging Kesalahan khusus SocketException-Broken Pipe apa pun selain level debug / jejak. Selain itu, ini dapat digunakan sebagai bentuk serangan DOS (Denial Of Service) dengan mengisi log. Coba dan tegaskan dan uji negatif aplikasi Anda untuk skenario umum ini.


Ini bukan disebabkan oleh rekan yang menutup koneksi saat Anda membaca darinya. Itu menyebabkan perbedaan, ujung aliran, kondisi, yang seringkali tidak terkecuali sama sekali.
Marquis of Lorne

5

Semua aliran terbuka & koneksi harus ditutup dengan benar, jadi lain kali kita mencoba menggunakan objek urlConnection, itu tidak menimbulkan kesalahan. Sebagai contoh, perubahan kode berikut memperbaiki kesalahan saya.

Sebelum:

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

Setelah:

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
Ini sebenarnya sangat aneh, karena BufferedOutputStreamselalu memanggil close()aliran output yang mendasarinya (sehingga pada aliran output urlConnection). Lihat docs.oracle.com/javase/6/docs/api/java/io/… dan docs.oracle.com/javase/6/docs/api/java/io/… Jadi ketika Anda menelepon os.close(), seharusnya sudah ditutup. . Tidak?
obecker

4
'os.close ()' bukan 'suatu keharusan'. Tidak ada 'out.close ()' dalam hal ini. 'bw.close ()' sudah cukup. @obedker benar. Perubahan ini sendiri tidak bisa memperbaiki masalahnya.
Marquis of Lorne

1

Saya telah mengimplementasikan fungsionalitas pengunduhan data melalui server FTP dan menemukan pengecualian yang sama di sana saat melanjutkan pengunduhan itu. Untuk mengatasi pengecualian ini, Anda harus selalu memutuskan koneksi dari sesi sebelumnya dan membuat instance baru dari Klien dan koneksi baru dengan server. Pendekatan yang sama ini dapat bermanfaat bagi HTTPClient juga.


Untuk mengatasi kesalahan ini, Anda harus menghindari mencoba menggunakan soket tertutup.
Marquis of Lorne

3
LOL. Anda bisa menghabiskan waktu seharian untuk memperbaiki semua saran palsu di SO.
Dave

2
@ Jangan Memang. Terkadang saya melakukannya.
Marquis of Lorne

1

Saya memiliki masalah yang sama ketika saya sedang mengembangkan aplikasi Java sederhana yang mendengarkan TCP tertentu. Biasanya, saya tidak punya masalah, tetapi ketika saya menjalankan beberapa stress test saya perhatikan bahwa beberapa koneksi terputus karena kesalahan socket write exception.

Setelah Investigasi saya menemukan solusi yang menyelesaikan masalah saya. Saya tahu pertanyaan ini cukup lama, tetapi saya lebih suka membagikan solusi saya, seseorang dapat menemukannya berguna.

Masalahnya adalah pada penciptaan ServerSocket. Saya membaca dari Javadoc ada batas default 50 pending socket. Jika Anda mencoba membuka koneksi lain, ini akan ditolak. Solusinya terdiri hanya dalam mengubah konfigurasi default ini di sisi server. Dalam kasus berikut, saya membuat server Socket yang mendengarkan pada port TCP 10_000dan menerima 200soket maksimum yang tertunda.

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();

Dari Javadoc of ServerSocket :

Panjang antrian maksimum untuk indikasi koneksi masuk (permintaan untuk terhubung) diatur ke parameter backlog. Jika indikasi koneksi tiba ketika antrian penuh, koneksi ditolak.


0

Masalahnya mungkin bahwa file yang Anda gunakan tidak diperbarui dengan metode RMI yang benar. Periksa untuk melihat bahwa antarmuka RMI Anda memiliki parameter yang diperbarui, atau struktur data yang diperbarui yang tidak dimiliki klien Anda. Atau klien RMI Anda tidak memiliki parameter yang berbeda dari yang dimiliki versi server Anda.

Ini hanya tebakan yang terpelajar. Setelah menyebarkan kembali file kelas aplikasi server saya dan pengujian ulang, masalah "Pipa rusak" hilang.


Apa metode RMI? RMI tidak disebutkan dalam pertanyaan.
Marquis of Lorne

0

Jawaban di atas menggambarkan alasan untuk ini java.net.SocketException: Broken pipe: ujung yang lain menutup koneksi. Saya ingin berbagi pengalaman tentang apa yang terjadi ketika saya mengalaminya:

  1. dalam permintaan klien, Content-Typetajuk secara keliru disetel lebih besar dari badan permintaan sebenarnya (sebenarnya tidak ada badan sama sekali)
  2. layanan bawah di tomcat socket menunggu data ukuran tubuh (http ada di TCP yang memastikan pengiriman dengan enkapsulasi dan ...)
  3. ketika 60 detik kedaluwarsa, tomcat melempar pengecualian: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. klien menerima respons dengan kode status 500 karena pengecualian batas waktu.
  5. klien menutup koneksi (karena menerima respons).
  6. tomcat melempar java.net.SocketException: Broken pipekarena klien menutupnya.

Kadang-kadang, kucing jantan tidak membuang pengecualian pip yang rusak, karena pengecualian batas waktu menutup koneksi, mengapa perbedaan seperti itu membingungkan saya juga.


The Content-TypeHeader tidak menentukan nomor, sehingga tidak bisa 'lebih besar' dari apa pun. Pengecualian batas waktu tidak menutup soket. Jawaban tidak masuk akal sama sekali.
Marquis of Lorne

@ EJP mungkin panjangnya konten, tidak ingat. Saya pikir batas waktu di Tomcat membuatnya kembali 500, sementara klien menerima respons ini dan menutup koneksi, yang akhirnya menyebabkan kucing jantan tidak menerima byte yang cukup seperti yang diharapkan.
Tiina

Jika Tomcat mengirim 500, ia sudah menerima semua yang direncanakannya. Masih tidak masuk akal.
Marquis of Lorne

@ user207421 tidak juga. Tomcat mengirim 500 karena tidak menerima semua yang diharapkannya, tetapi kehabisan waktu.
Tiina

-1

JavaDoc:

Panjang antrian maksimum untuk indikasi koneksi masuk (permintaan untuk terhubung) diatur ke 50. Jika indikasi koneksi tiba ketika antrian penuh, koneksi ditolak.

Anda harus meningkatkan "backlog" parameter ServerSocket Anda, misalnya

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

1
Meningkatkan backlog tidak akan memecahkan kesalahan 'pipa rusak'.
Marquis of Lorne

1
tetapi itu membantu saya
TheSecond

@ EJP Saya menguji server di Linux dan berhasil, tetapi pada Mac OS - tidak. Dan meningkatkan ukuran tumpukan membantu saya. Saya tidak tahu bahwa ukuran maksimal adalah 50.
TheSecond

1
Anda menguji sesuatu yang lain. Mendengarkan backlog tidak ada hubungannya dengan pengecualian ini. Ini membantu penolakan koneksi atau timeout pada klien. Bukan pengecualian 'soket tertutup', yang merupakan kesalahan lokal.
Marquis of Lorne

Untuk kumpulan soket (seperti server HTTP seperti Tomcat) ada pengaturan konfigurasi untuk jumlah "penerimaan" yang tertunda, server akan "antri" sebelum mengirim utvi servis dan pengaturan terpisah untuk jumlah utas di pool. Namun, semua ini tidak terkait dengan masalah bahwa setelah Server "accept ()" ketika koneksi dibuat - aliran output tiba-tiba (tanpa disadari) "ditutup".
Darrell Teague

-1

Penyebabnya adalah bahwa rekan jauh menutup soketnya (crash misalnya), jadi jika Anda mencoba menulis data kepadanya misalnya, Anda akan mendapatkan ini. untuk mengatasinya, lindungi operasi baca / tulis ke soket di antara (coba tangkap), lalu kelola kasus koneksi terputus ke tangkap (perpanjang koneksi, hentikan program, ... dll):

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

3
Ini akan log yang masalah, tapi itu tidak akan memecahkan mereka. Untuk menyelesaikannya, Anda harus (a) menyelesaikan kesalahan protokol aplikasi yang mungkin dan (b) menutup koneksi mati. Bukan hanya mencatat pengecualian, yang sebagian besar berakibat fatal.
Marquis of Lorne

@ EJP: tentu saja, ketika Anda menangkap kesalahan, Anda harus membersihkan soket Anda (konteks) ke dalam pernyataan penangkapan. Saya tidak bisa menebak apa yang akan dilakukan pengembang. itu sendiri untuk melakukan prosedur bersih
elhadi dp ıpɐɥ ן ǝ

Terserah pengembang untuk memperbaiki masalah protokol aplikasi yang menyebabkan kesalahan, dan ada. Tidak ada yang saya jawaban atau kode Anda yang membantunya. lakukan itu. 'Sisipkan operasi baca / tulis di antara' tidak memperbaikinya. Jawaban Anda salah.
Marquis of Lorne

@ user207421, pengembang bertanya bagaimana memperbaiki pipa yang rusak. Saya telah mengindikasikan kepadanya untuk melindungi programnya terlebih dahulu dengan menggunakan (coba tangkap). ini akan menghindari program macet. kemudian saya memasukkan komentar untuk menunjukkan kepadanya untuk melakukan pemrosesan bersih. Secara umum dalam kesalahan pipa rusak, ada banyak alternatif, saya tidak bisa menebak tujuan program, menghentikan program, memperbarui koneksi, data rusak atau tidak .... dll. terserah kepada programmer untuk memutuskan opsi mana yang harus dilakukan? apa yang salah dengan jawabanku?
elhadi dp ıpɐɥ ן ǝ
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.