Java.net.SocketException: Kırık boru nasıl düzeltilir?


150

Ben parametreleri göndermek için post yöntemini kullanarak url aramak için apache commons http istemci kullanıyorum ve nadiren aşağıdaki hata atıyor.

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)

Birisi bu İstisnaya neyin sebep olduğunu ve nasıl hata ayıklayacağını önerebilir mi?


Yanıtlar:


81

Bunun nedeni:

  • çoğunlukla, diğer uç zaten kapalıyken bir bağlantıya yazma;
  • daha az genellikle, akran sonunda bekleyen tüm verileri okumadan bağlantıyı kapatır.

Yani her iki durumda da kötü tanımlanmış veya uygulanmış bir uygulama protokolüne sahipsiniz.

Burada belgelememem ancak eşin bağlantıyı düzgün bir şekilde kapatmak yerine sıfırlamak için kasıtlı eylemde bulunmasını gerektiren üçüncü bir neden var.


4
Lütfen tanımlamam ve düzeltmeme yardımcı olabilir misiniz? Temel olarak, neden doğrulanır ve düzeltilir?

4
Ben var nedeni doğruladı. Diğer uç bağlantıyı kapatmışken yazıyorsunuz. Düzeltme bunu yapmak değil. Diğer uç okumadığından, hiçbir anlamı yok. Söylediğim gibi, eğer bu oluyorsa, uygulama protokolü belirtiminiz veya uygulamanızla ilgili bir sorun var, muhtemelen bir tane bile yok.
Lorne Marquis

26
Sunucu tarafı HTTP uygulaması Kırık Boru istisnaları alıyorsa, bu sadece istemci tarayıcısının çıktığı / başka bir sayfaya gittiği / zaman aşımına uğradığı / geçmişe geri döndüğü / ne olduğu anlamına gelir. Sadece unut gitsin.
Lorne Marquis

3
Tarayıcıda bir ajax isteğini iptal ederken (kullanarak XMLHttpRequest.abort()) bu istisnayı gördük .
obecker

2
Olası nedenlerden biri, bağlantıyı çok erken kapatan bir proxy veya Http sunucusu olabilir. Örneğin, J2EE sunucunuz ile uygulama istemcileri arasında kısa bir zaman aşımı süresi olan bir Apache Http sunucusu. En azından üretim ortamımızda olan buydu. Müşterilerin sayfaların tamamen yüklenmediğinden şikayet ettikleri için bu hataları JBoss günlüğünde gördük. Bazı testlerden sonra sorunun kötü yapılandırılmış bir Http Sunucusu olduğunu fark ettik.
Ricardo Vila

32

Bizim durumumuzda, uygulama sunucumuzda bir yük testi gerçekleştirirken bunu yaşadık. Sorun, tükendiği için JVM'ye ek bellek eklememiz gerektiği ortaya çıktı. Bu sorunu çözdü.

JVM'nin kullanabileceği belleği artırmayı deneyin veya bu hataları aldığınızda bellek kullanımını izleyin.


4
Aynı sorunu bir yük testi sırasında aldım. Verilerin oluşturulması çok zaman alıyor ve yük testi aracı veriyi yeterince beklemiyor, sonra bağlantıyı kapatıyor. Sizin durumunuzda, bellek eklemek veri oluşturma işlem süresini azaltmış olabilir, böylece yük testi zaman aşımı sınırı olmadan tüm verileri alır. Belleği artırmak için bir alternatif, yük testi aracı zaman aşımını arttırmak olabilirdi.
Julien Kronegg

2
Düşük bellek, uygulamanın alıcı soketi kapatmasına veya gerçekten de aynı etkiyle tamamen çıkmasına neden oldu. Düşük bellek tek başına kırık borulara neden olmaz.
Lorne Marquis

1
bu bizim ağır yük uygulamamız sırasında da bir sorun gibi görünüyor
Ruben de Vries

7

SocketException: Bozuk boru, kodunuz bağlantıdan okurken veya bağlantıya yazarken bağlantıyı kapatan 'diğer uç' (istemci veya sunucu) nedeniyle oluşur.

Bu, uygulama denetimi dışındaki istemcilerden veya sunuculardan trafik alan istemci / sunucu uygulamalarında çok yaygın bir istisnadır. Örneğin, istemci bir tarayıcıdır. Tarayıcı bir Ajax çağrısı yaparsa ve / veya kullanıcı sayfayı veya tarayıcıyı kapatırsa, bu durum tüm iletişimi beklenmedik bir şekilde etkili bir şekilde öldürebilir. Temel olarak, diğer uç uygulamalarını sonlandırdığında bu hatayı görürsünüz ve siz bunu beklemiyordunuz.

Uygulamanızda bu Kural Dışı Durumla karşılaşırsanız, bu, IO'nun (Giriş / Çıkış) oluştuğu kodunuzu kontrol etmeniz ve bu IOException'ı yakalamak için bir try / catch bloğu ile sarmanız gerektiği anlamına gelir. Bu yarı geçerli durumu nasıl ele almak istediğinize karar vermek size kalmıştır.

Sizin durumunuzda, hala kontrol sahibi olduğunuz en eski yer çağrıdır HttpMethodDirector.executeWithRetry- Bu yüzden çağrının try / catch bloğu ile sarıldığından emin olun ve uygun gördüğünüz şekilde işleyin.

Kesinlikle hata ayıklama / izleme düzeyleri dışında bir şey SocketException-kırık boru belirli hataları günlüğe karşı öneriyoruz. Aksi takdirde, bu, günlükleri doldurarak bir DOS (Hizmet Reddi) saldırısı biçimi olarak kullanılabilir. Bu yaygın senaryo için başvurunuzu deneyin ve sertleştirin ve negatif test edin.


Bunun nedeni, sizden okurken bağlantının kapatılmasından kaynaklanmamasıdır. Bu, genellikle bir istisna olmayan farklı bir akış sonu durumuna neden olur.
Lorne Marquis

5

Tüm açık akışların ve bağlantıların düzgün bir şekilde kapatılması gerekir, bu nedenle urlConnection nesnesini bir sonraki kullanmaya çalıştığımızda hata atmaz. Örnek olarak, aşağıdaki kod değişikliği benim için hatayı düzeltti.

Önce:

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

Sonra:

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
Bu aslında çok garip, çünkü BufferedOutputStreamher zaman close()altta yatan çıkış akışını çağırır (böylece çıkış akışını urlConnection). Bkz. Docs.oracle.com/javase/6/docs/api/java/io/… ve docs.oracle.com/javase/6/docs/api/java/io/… Bu nedenle, aradığınızda os.close()zaten kapatılmış olmalıdır . Hayır?
obecker

4
'os.close ()' 'olmazsa olmaz' değildir. Bu konu için 'out.close ()' da yoktur. 'bw.close ()' yeterlidir. @obedker doğru. Bu değişiklik tek başına sorunu çözemez.
Lorne Marquis

1

FTP sunucusu üzerinden veri indirme işlevselliği uyguladım ve bu indirmeyi sürdürürken de aynı istisnayı buldum. Bu özel durumu gidermek için, her zaman önceki oturumla bağlantıyı kesmeniz ve İstemcinin yeni bir örneğini ve sunucuyla yeni bir bağlantı oluşturmanız gerekir. Aynı yaklaşım HTTPClient için de yararlı olabilir.


Bu hatayı gidermek için kapalı soketleri kullanmaya çalışmaktan kaçınmalısınız.
Lorne

3
LOL. SO üzerinde tüm sahte tavsiye düzeltme tüm gün boşa harcayabilirsiniz.
Dave

2
@Dave Gerçekten. Bazen yaparım.
Lorne Marquis

1

Belirli bir TCP'yi dinleyen basit bir Java uygulaması geliştirirken de aynı sorunu yaşarım. Genellikle hiçbir sorunum olmadı, ancak bazı stres testi yaptığımda bazı bağlantıların hata ile kesildiğini fark ettim socket write exception.

Soruşturmadan sonra sorunumu çözen bir çözüm buldum. Bu sorunun oldukça eski olduğunu biliyorum, ancak çözümümü paylaşmayı tercih ediyorum, birisi faydalı bulabilir.

Sorun ServerSocket oluşturma işlemindeydi. Javadoc okudum varsayılan bekleyen 50 priz sınırı vardır. Başka bir bağlantı açmayı denerseniz bunlar reddedilir. Çözüm, sunucu tarafında bu varsayılan yapılandırmayı değiştirmekten ibarettir. Aşağıdaki durumda, TCP bağlantı noktasını dinleyen 10_000ve maksimum 200bekleyen soketleri kabul eden bir Soket sunucusu oluşturuyorum .

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

Bir Javadoc itibaren ServerSocket :

Gelen bağlantı göstergeleri (bağlantı isteği) için maksimum kuyruk uzunluğu biriktirme listesi parametresine ayarlanır. Sıra dolduğunda bir bağlantı göstergesi gelirse, bağlantı reddedilir.


0

Sorun, konuşlandırılan dosyalarınızın doğru RMI yöntemleriyle güncellenmemesidir. RMI arabiriminizde güncelleştirilmiş parametreler veya istemcinizin sahip olmadığı güncellenmiş veri yapıları olup olmadığını kontrol edin. Veya RMI istemcinizin sunucu sürümünüzden farklı hiçbir parametresi olmadığını.

Bu sadece eğitimli bir tahmindir. Sunucu uygulamamın sınıf dosyalarını yeniden dağıttıktan ve yeniden test ettikten sonra, "Kırık boru" sorunu ortadan kalktı.


Hangi RMI yöntemleri? Soruda RMI belirtilmemiştir.
Lorne Marquis

0

Yukarıdaki cevaplar bunun nedenini göstermektedir java.net.SocketException: Broken pipe: diğer uç bağlantıyı kapattı. Karşılaştığımda olanları paylaşmak istiyorum:

  1. bir müşterinin isteğinde, Content-Typebaşlık yanlışlıkla istek gövdesinin gerçekte olduğundan daha büyük ayarlanır (aslında hiç gövde yoktu)
  2. Tomcat soketindeki alt servis o büyüklükteki gövde verilerini bekliyordu (http TCP'de kapsülleme ve ...
  3. 60 saniye dolduğunda tomcat zaman aşımı istisnası atar: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception java.net.SocketTimeoutException: null
  4. istemci, zaman aşımı istisnası nedeniyle durum kodu 500 ile bir yanıt alır.
  5. istemci yakın bağlantısı (yanıt aldığı için).
  6. Tomcat atıyor java.net.SocketException: Broken pipeçünkü müşteri kapadı.

Bazen, tomcat kırık pip istisnası atmaz, çünkü zaman aşımı istisnası bağlantıyı kapatır, neden böyle bir fark beni karıştırıyor.


Content-TypeO şey daha 'büyük' olamaz böylece başlık, bir sayı belirtmez. Zaman aşımı istisnaları soketi kapatmaz. Cevap hiç mantıklı değil.
Lorne Marquis

@EJP belki içerik uzunluğuydu, hatırlamıyorum. Tomcat'teki zaman aşımı 500'ü geri döndürdüğünü düşünüyorum, istemci bu yanıtı alır ve bağlantıyı kapatır, bu da sonuçta tomcat'in beklendiği gibi yeterli bayt almamasına neden olur.
Tiina

Tomcat 500 gönderirse, planladığı her şeyi zaten aldı. Hala mantıklı değil.
Lorne Marquis

@ user207421 gerçekten değil. Tomcat beklediği her şeyi almadığı için zaman aşımına uğradığı için 500 gönderiyor.
Tiina

-1

javadoc:

Gelen bağlantı göstergeleri (bağlantı isteği) için maksimum kuyruk uzunluğu 50 olarak ayarlanır. Kuyruk dolduğunda bir bağlantı göstergesi gelirse, bağlantı reddedilir.

Örneğin, ServerSocket sunucunuzun "biriktirme listesi" parametresini artırmalısınız

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

1
İş birikiminin artırılması 'kırık boru' hatasını çözmez.
Lorne Marquis,

1
ama bana yardımcı oldu
TheSecond

@EJP Linux'ta bir sunucuyu test ettim ve çalıştı, ama Mac OS'de değil. Ve birikmiş işler büyüklüğünün artması bana yardımcı oldu. Maksimum boyutun 50 olduğunu bilmiyordum.
TheSecond

1
Başka bir şey test ettin. Dinleme biriktirme listesi bu istisna ile ilgisi yoktur. İstemcide bağlantı reddi veya zaman aşımlarına yardımcı olur. Yerel bir hata olan 'soket kapalı' istisnaları değil.
Lorne

Yuva havuzları için (Tomcat gibi bir HTTP sunucusu gibi) bekleyen "kabul" sayısı için yapılandırma ayarları vardır. Sunucunun servis iş parçacıklarını göndermeden önce "kuyruğa" girmesi ve havuzdaki iş parçacığı sayısı için ayrı bir ayar vardır. Ancak, bunların hiçbiri Sunucu kurulduktan sonra bağlantı kabul edildiğinde "kabul ()" - çıkış akışının aniden (farkında olmadan) "kapatılması" sorunuyla ilgili değildir .
Darrell Teague

-1

Bunun nedeni, uzak eşin soketini kapatmasıdır (örneğin kilitlenme), bu yüzden örneğin ona veri yazmaya çalışırsanız, bunu elde edersiniz. bunu çözmek için, okuma / yazma arasındaki soket işlemlerini koruyun (yakalamayı deneyin), ardından bağlantıyı kaybetme durumunu yakalamaya yönetin (bağlantıyı yenileyin, programı durdurun, vb.):

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

3
Bu olacak log sorunları, ama olmaz çözmek onları. Bunları çözmek için (a) olası uygulama protokol hatalarını çözmeniz ve (b) ölü bağlantıları kapatmanız gerekir . Çoğu ölümcül olan sadece günlük istisnaları değil.
Lorne Marquis

@EJP: Tabii ki, bir hata yakaladığınızda soketinizi (bağlam) catch deyimine temizlemelisiniz. Geliştiricinin ne yapacağını tahmin edemiyorum. temiz prosedürü yapmak kendi başına
elhadi dp ıpɐɥ ן ǝ

Hataya neden olan uygulama protokolü sorununu düzeltmek geliştiriciye bağlıdır ve vardır. Hiçbir şey ben ona cevap ya da kod yardımcı olur. böyle yap. 'Arasına okuma / yazma işlemi ekleme' sorunu çözmez. Cevabınız yanlış.
Lorne Marquis

@ user207421, geliştirici kırık borunun nasıl düzeltileceğini sorar. Ben öncelikle (programını deneyin) kullanarak programı korumak için ona gösterdik. bu programın çökmesini önleyecektir. sonra ona temiz işleme yapmasını belirtmek için bir yorum ekliyorum. Genellikle kırık boru hataları, birçok alternatif vardır, ben programın amacı tahmin edemez, programı durdurmak, bağlantı yenilemek, veri bozuk ya da değil .... vb. hangi seçeneğin yapılacağına karar vermek programcıya bağlıdır? cevabımın nesi vardı?
elhadi dp ıpɐɥ ן ǝ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.