NodeJS - “Soket telefonu kapatmak” aslında ne anlama geliyor?


276

Düğüm ve Cheerio ile bir web kazıyıcı inşa ediyorum ve belirli bir web sitesi için aşağıdaki hatayı alıyorum (sadece bu web sitesinde olur, kazımayı denediğim başkaları yok.

Her seferinde farklı bir yerde olur, bu yüzden bazen url xhatayı atar, diğer zamanlar url xiyidir ve tamamen farklı bir url'dir:

    Error!: Error: socket hang up using [insert random URL, it's different every time]

Error: socket hang up
    at createHangUpError (http.js:1445:15)
    at Socket.socketOnEnd [as onend] (http.js:1541:23)
    at Socket.g (events.js:175:14)
    at Socket.EventEmitter.emit (events.js:117:20)
    at _stream_readable.js:910:16
    at process._tickCallback (node.js:415:13)

Hata ayıklamak çok zor, nereden başlayacağımı gerçekten bilmiyorum. Başlamak için ne IS bir yuva hatası asmak? 404 hatası mı yoksa benzeri mi? Yoksa sadece sunucunun bir bağlantıyı reddettiği anlamına mı geliyor?

Bunun hiçbir yerinde bir açıklama bulamıyorum!

EDIT: İşte (bazen) hataları döndüren bir kod örneği:

function scrapeNexts(url, oncomplete) {
    request(url, function(err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        $ = cheerio.load(body);
        // do stuff with the '$' cheerio content here
    });
}

Bağlantıyı kapatmak için doğrudan bir çağrı yoktur, ancak Node Requesthangisini (anlayabildiğim kadarıyla) kullandığı için kullanıyorum http.get, bu gerekli değildir, eğer yanılıyorsam beni düzeltin!

DÜZENLEME 2: İşte hatalara neden olan gerçek, kullanımda bir kod parçası. prodURLve diğer değişkenler çoğunlukla daha önce tanımlanan jquery seçicileridir. Bu, asyncDüğüm için kitaplığı kullanır .

function scrapeNexts(url, oncomplete) {
    request(url, function (err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        async.series([
                function (callback) {
                    $ = cheerio.load(body);
                    callback();
                },
                function (callback) {
                    $(prodURL).each(function () {
                        var theHref = $(this).attr('href');
                        urls.push(baseURL + theHref);
                    });
                    var next = $(next_select).first().attr('href');
                    oncomplete(next);
                }
            ]);
    });
}

26
Bu, soketin endzaman aşımı süresi içinde bağlantı olayı göndermediği anlamına gelir . http.request(Değil http.get) üzerinden cheerio için istek alıyorsanız . request.end()İsteği göndermeyi bitirmek için aramak zorundasınız .
user568109

1
@ user568109 Not etmeliyim, requestbelirli bir http.requestistek değil , düğüm hizmetini kullanıyorum (sanırım, düğüm için çok yeniyim!). Bu: github.com/mikeal/request Bu, isteği otomatik olarak bitirmiş gibi görünüyor, değil mi? EDIT: Belgelere göre http method, defaults to GET, sorun bu değil.
JVG

2
O zaman sorun olmamalı. Cheerio.load dahil olmak üzere kazıma bölümüne yorum yapar ve aynı içeriği döndürürseniz ne olur? Buradaki yakalama cheerio.load, zaman uyumsuzdur. Yani $ ile bir şeyler yapmaya başlamadan önce bitmeyebilir.
user568109

4
Ayrıca bazen bir siteyi çok agresif bir şekilde tararsam (10'dan fazla eşzamanlı bağlantı gibi) soket takılmalarıyla yanıt vermeye başlayacaklarını buldum, bu da olabilir.
tobek

1
İngilizce olarak sadece FYI, bağlantıyı keserek elektronik konuşmayı bitirmekhang up anlamına gelir ; eski moda telefonu asmaktan kaynaklanıyor.
Константин Ван

Yanıtlar:


161

socket hang upAtılan iki durum vardır :

Müşteri olduğunuzda

İstemci olarak, uzak bir sunucuya istek gönderdiğinizde ve zamanında yanıt alamadığınızda. Bu hatayı atan soketiniz bitti. Bu hatayı yakalamanız ve nasıl ele alacağınıza karar vermeniz gerekir: isteği yeniden deneyin, daha sonra kuyruğa geçirin.

Sunucu / proxy olduğunuzda

Bir sunucu olarak, belki de bir proxy sunucusundan, bir istemciden bir istek aldığınızda, daha sonra ona göre hareket etmeye başladığınızda (veya isteği yukarı akış sunucusuna ilettiğinizde) ve yanıtı hazırlamadan önce, istemci iptal etmeye / iptal etmeye karar verir talep.

Bu yığın izlemesi, istemci isteği iptal ettiğinde ne olacağını gösterir.

Trace: { [Error: socket hang up] code: 'ECONNRESET' }
    at ClientRequest.proxyError (your_server_code_error_handler.js:137:15)
    at ClientRequest.emit (events.js:117:20)
    at Socket.socketCloseListener (http.js:1526:9)
    at Socket.emit (events.js:95:17)
    at TCP.close (net.js:465:12)

Hat http.js:1526:9aynı puan socketCloseListener, özellikle, @Blender yukarıda belirtilen:

// This socket error fired before we started to
// receive a response. The error needs to
// fire on the request.
req.emit('error', createHangUpError());

...

function createHangUpError() {
  var error = new Error('socket hang up');
  error.code = 'ECONNRESET';
  return error;
}

İstemci tarayıcıda bir kullanıcı ise bu tipik bir durumdur. Bazı kaynak / sayfa yükleme isteği uzun sürer ve kullanıcılar sayfayı yeniler. Bu tür eylem, sunucu tarafında bu hatayı atan önceki isteğin iptal edilmesine neden olur.

Bu hata bir istemcinin isteğinden kaynaklandığından, herhangi bir hata mesajı almayı beklemezler. Bu nedenle, bu hatayı kritik olarak düşünmeye gerek yoktur. Görmezden gel. Bu, böyle bir hatada, resmüşterinizin dinlediği soketin hala yazılabilir olmasına rağmen yok edilmesi gerçeğiyle teşvik edilir .

console.log(res.socket.destroyed); //true

Bu nedenle, yanıt nesnesini açıkça kapatmak dışında hiçbir şey göndermenin anlamı yoktur:

res.end();

Ancak, ne yapmalıyım eğer kesin olan zaten yukarı yönüne isteği geçirilen bir proxy sunucusu, sırayla ust baş söyleyecektir yanıt olarak ilgi eksikliği, belirten yukan iç talep iptal etmektir sunucu, pahalı bir işlemi durdurmak için.


2
Müşteri olarak isteği daha uzun süre nasıl bekletebilirim? 35 saniyede hata veriyor ve yaklaşık bir dakika beklemem gerekiyor.
Büyük Para

Aynı sorunla karşı karşıyayım. Yanıt beklemek ve tek tek yürütme gibi bir sonraki isteği göndermeye başlamak mümkün mü? Bu soketi nasıl kullanacağımı öğrenebilir miyim?
Deepak

@BigMoney kullanabilirsiniz setTimeout(). bu soruya bakın: stackoverflow.com/questions/6214902/…
holla

Bilgileriniz cehennemden kurtuldu, yukarı akış sunucusu ve istemci arasında bir proxy sunucusu olarak node.js kullanıyordum, istek üzerine zaman aşımı bu hatayı atmayı unuttuğum için attı res.send, teşekkürler
Farzad YZ

Aynı bağlantı üzerinden bir Django'nun geliştirme web sunucusuna ikinci bir istekte bulunmaya çalıştığınızda istemci olarak "soket telefonu kapat" ı alabilirsiniz. Desteklemiyor keep-alive. Müşterinizin bunu beklemesi durumunda, hatayı alırsınız. Aşağıdaki çizgiler boyunca görünüyor .
x-yuri

53

Kaynağa bir göz atın :

function socketCloseListener() {
  var socket = this;
  var parser = socket.parser;
  var req = socket._httpMessage;
  debug('HTTP socket close');
  req.emit('close');
  if (req.res && req.res.readable) {
    // Socket closed before we emitted 'end' below.
    req.res.emit('aborted');
    var res = req.res;
    res.on('end', function() {
      res.emit('close');
    });
    res.push(null);
  } else if (!req.res && !req._hadError) {
    // This socket error fired before we started to
    // receive a response. The error needs to
    // fire on the request.
    req.emit('error', createHangUpError());
    req._hadError = true;
  }
}

Sunucu hiçbir zaman yanıt göndermediğinde mesaj gönderilir.


2
İşlevsel bir bakış açısından bunun ne anlama geldiğini açıklayabilir misiniz? Burada bir dizi rahatsız edici url'leri ekleyerek ve daha sonra onları kazıyarak korumalar oluşturmak çalışıyorum. Birkaç yerde hataların Düğümle ilgili bir kuyruk sorunu olabileceğini okudum, bunu düzeltmenin ve önlemenin en iyi yolunu bilmiyorum.
JVG

5
Ama ne kadar bekleyecek?
CommaToast

2
"Ne kadar süre" uygulanması için en.wikipedia.org/wiki/Exponential_backoff komutunu kullanmalıdır .
Norman H

bu "soket takmak" anlamsızdır. Bu sadece nodejs ekibinden bir sürpriz.
puchu

45

Bahsetmeye değer bir örnek: Node.js'den Express kullanarak Node.js'ye bağlanırken, istenen URL yoluna "/" öneki eklemezsem "soket kapat" ı alıyorum.


1
benim sorunumdu, hem istemci hem de sunucu saf http node.js'de
ashley willis

1
@silentorb: Lütfen örnek url gösterebilir misiniz? Bu durumda aynı hatayla karşılaşıyorum .. Teşekkürler.
Pritam

4
Hata: "kullanıcı / giriş", Başarı: "/ kullanıcı / giriş"
silentorb

4
Adamım hata ayıklamak için neredeyse bir saat geçirdim! Cevabınızı gördüm ve SH ** düşündüm / ekledi ve iyi çalışıyor :) teşekkürler!
Daniel Gruszczyk

4
Bu cevapla bana saatler kazandın!
imhotep

32

Ben https hizmet require('http')tüketiyordu ve " " gösterdi .socket hang up

Sonra değişti require('http')etmek require('https')yerine, ve çalışıyor.


Bu soruna bir çözüm olsa da, sorunun cevabı değildir . Poster, hata mesajının anlamı hakkında bir cevap istedi. Ayrıca, zaten çok sayıda yüksek kaliteli cevap var. Sizinki ek değer sağlamaz.
Johannes Dorn

19
Yorumunuz için teşekkürler. Bu hata için zamanımı boşa harcıyorum. Son olarak, sadece bu çözümü deniyorum ve işe yarıyor. Sadece paylaşmak istiyorum. Diğerlerinin zamanını boşa harcamamasını, yüksek kaliteli cevap olarak övgü almamasını umuyoruz.
Aekkawit Chanpen

12
@JohannesDorn Bu, hatanın ne anlama geldiği sorusunun örtük bir cevabıdır. Ve yararlı bir tane.
Ulad Kasach

29

Aşağıda, aşağıdaki örnekte yorumlanan kodu eklemeyi kaçırdığımda aynı hatayı aldığım basit bir örnek. Kodun kaldırılması req.end()bu sorunu çözecektir.

var fs = require("fs");
var https = require("https");

var options = {
    host: "en.wikipedia.org",
    path: "/wiki/George_Washington",
    port: 443,
    method: "GET"
};

var req = https.request(options, function (res) {
    console.log(res.statusCode);
});


// req.end();

2
Bu aklımı kurtardı ... Teşekkürler!
PGallagher

Sen bir kahramansın! Teşekkür ederim.
Xenhat

17

Blender'ın cevabına genişleyerek, bu birkaç durumda olur. Karşılaştığım en yaygın olanları:

  1. Sunucu çöktü.
  2. Sunucu bağlantınızı reddetti, büyük olasılıkla tarafından engellendi User-Agent.

socketCloseListener, Blender'ın cevabında belirtildiği gibi, kapatma hatalarının oluşturulduğu tek yer değildir.

Örneğin, burada bulundu :

function socketOnEnd() {
  var socket = this;
  var req = this._httpMessage;
  var parser = this.parser;

  if (!req.res) {
    // If we don't have a response then we know that the socket
    // ended prematurely and we need to emit an error on the request.
    req.emit('error', createHangUpError());
    req._hadError = true;
  }
  if (parser) {
    parser.finish();
    freeParser(parser, req);
  }
  socket.destroy();
}

curlDüğümden gönderilen üstbilgileri ve bu tür deneyleri deneyebilir ve orada yanıt alıp almadığınızı görebilirsiniz. İle bir yanıt curlalmazsanız, ancak tarayıcınızda bir yanıt alırsanız, User-Agentbaşlığınız büyük olasılıkla engellenir.


3
Sunucunuzun bağlantınızı reddedebilmesinin başka bir nedeni de (QA yerine prod'a geçerken buna tıklıyorum), sunucunuz http yerine bir https isteği bekliyorsa.
mcole

7

Bahsetmeye değer başka bir örnek (Linux ve OS X için), httpsistekleri yerine getirmek için bir kütüphane kullanırsanız veya https://...yerel olarak sunulan örneğin URL'si olarak geçerseniz, 443ayrılmış bir özel bağlantı noktası olan bağlantı noktasını kullanacaksınız ve bitiyor olabilir Socket hang upveya ECONNREFUSEDhatalar olabilir.

Bunun yerine, port 3000, fe ve bir httpistek kullanın.


6

Couch DB'ye bağlanmak için Nano kütüphaneyi kullanırken de aynı sorunu yaşadım . Keepaliveagent kütüphane kullanımı ile bağlantı havuzu ince ayar yapmaya çalıştı ve soket kapatma mesajı ile başarısız tuttu .

var KeepAliveAgent = require('agentkeepalive');

var myagent = new KeepAliveAgent({
    maxSockets: 10,
    maxKeepAliveRequests: 0,
    maxKeepAliveTime: 240000
});

nano = new Nano({
    url : uri,
    requestDefaults : {
        agent : myagent
    }
});

Biraz uğraştıktan sonra sorunu çiviledim - ortaya çıktıkça çok, çok basit bir hataydı. HTTPS protokolü aracılığıyla veritabanına bağlanmaktaydı, ancak nano nesnesime bu kütüphane şovunun kullanım örnekleri olarak oluşturulan bir keepalive aracısı geçmeye devam ettim (http kullanan bazı varsayılanlara güveniyorlar).

HttpsAgent'ı kullanmak için basit bir değişiklik hile yaptı:

var KeepAliveAgent = require('agentkeepalive').HttpsAgent;

1
Biraz daha fazla ayrıntı için, istek bağlantı noktası 443 için yapılandırılırsa ve istek https modülü yerine http modülü aracılığıyla verilirse, bir yuva kapatırsınız. Bağlantı kesilmesinin neden olduğu hakkında daha fazla ayrıntı olması güzel olurdu (SSL / TLS anlaşması?). ASP.NET'te bu ayrıntı düzeyini gördüm.
Richard Collette

6

Burada listelenen her şeyi yaptığım için bu bana sorunlara neden oldu, ancak yine de hatalar atıldı. Görünüşe göre req.abort () çağrısı aslında ECONNRESET koduyla bir hata atıyor, bu yüzden bunu hata işleyicinizde yakalamak zorundasınız.

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        return;
    }
    //handle normal errors
});

5

İçin requestmodül kullanıcıları

Zaman aşımları

İki ana zaman aşımı türü vardır: bağlantı zaman aşımları ve okuma zaman aşımları . İstemciniz uzaktaki bir makineyle bağlantı kurmaya çalışırken (prizdeki çağrıya karşılık gelir) zamanaşımı gerçekleşirse bir bağlantı zaman aşımı oluşur connect(). Sunucu yanıtın bir bölümünü geri göndermek için çok yavaş olduğunda bir okuma zaman aşımı oluşur.

Not o bağlantı zaman aşımları bir yayarlar ETIMEDOUThatası ve okuma zaman aşımları bir yayarlar ECONNRESEThatası.


3

Bazı sunucuya istek sırasında aynı sorunu vardı. Benim durumumda, istek seçeneklerinde başlıklarda Kullanıcı Aracısı olarak herhangi bir değer ayarlamak bana yardımcı oldu.

const httpRequestOptions = {
    hostname: 'site.address.com',
    headers: {
       'User-Agent': 'Chrome/59.0.3071.115'
    }
};

Bu genel bir durum değildir ve sunucu ayarlarına bağlıdır.


2

Ayrıca nedeni , ' den yerine' appörneği kullanmak olabilir .expressserverconst server = http.createServer(app) sunucu soketi oluşturulurken.

Yanlış

const express = require('express');
const http = require('http');
const WebSocket = require('ws');


const app = express();

app.use(function (req, res) {
  res.send({ msg: "hello" });
});

const wss = new WebSocket.Server({ server: app }); // will throw error while connecting from client socket

app.listen(8080, function listening() {
  console.log('Listening on %d', server.address().port);
});

Doğru

const express = require('express');
const http = require('http');
const WebSocket = require('ws');


const app = express();

app.use(function (req, res) {
  res.send({ msg: "hello" });
});

const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

server.listen(8080, function listening() {
  console.log('Listening on %d', server.address().port);
});

1

Hem web (düğüm) hem de Android geliştirme yapıyorum ve Android Studio cihaz simülatörünü ve docker'ı birlikte açıyorum, her ikisi de 8601 numaralı bağlantı noktasını kullanıyor socket hang up, Android Studio cihaz simülatörünü kapattıktan sonra hatadan şikayet ediyor ve düğüm tarafında iyi çalışıyor. Android Studio cihaz simülatörünü ve docker'ı birlikte kullanmayın.


1

OCP kümesinde CouchDB kullanırken benzer bir hata aldım.

const cloudantSessionStore = sessionStore.createSessionStore(
  {
    type: 'couchdb',
    host: 'https://' + credentials['host'],
    port: credentials['port'],
    dbName: 'sessions',
    options: {
      auth: {
        username: credentials['username'],
        password: credentials['password']
      },
      cache: false
    }
  }

Benim CouchDB örneği ile bağlanmak için "http" değil, "https" olmalıdır. Umarım benzer bir sorunla karşılaşan herkes için yararlı olabilir.


0

Benim durumumda, bir uygulama / json yanıtı kötü biçimlendirilmişti (yığın izlemesi içeriyor). Yanıt hiçbir zaman sunucuya gönderilmedi. Hata ayıklamak çok zordu çünkü günlük yoktu. Bu konu ne olduğunu anlamama yardımcı oluyor.



0

Dün Webli ve node.js sunucumu IntelliJ IDEA 2016.3.6 aracılığıyla çalıştırarak bu sorunla karşılaştım. Tek yapmam gereken Chrome tarayıcımdaki çerezlerimi ve önbelleğimi temizlemekti.


0

Bu hatayı bir https bağlantısı üzerinden yaşıyorsanız ve anında gerçekleşiyorsa, SSL bağlantısını kurarken bir sorun olabilir.

Benim için bu sorun https://github.com/nodejs/node/issues/9845 oldu ama sizin için başka bir şey olabilir. Eğer ssl ile ilgili bir sorun varsa, sadece etki alanına bağlanmaya çalışan nodejs tls / ssl paketi ile çoğaltabilmelisiniz.


0

Bence kayda değer ...

Google API'ları için testler yapıyordum. Bir geçici sunucu ile isteği durduruyordum, sonra bunları gerçek api'ye yönlendiriyordum. İstekte sadece üstbilgileri geçmeye çalışıyordum, ancak birkaç üstbilgi diğer tarafta ifade ile ilgili bir soruna neden oluyordu.

Yani, ben silmek zorunda connection, acceptve content-lengthbaşlıklar boyunca iletmek için istek modülünü kullanmadan önce.

let headers = Object.assign({}, req.headers);
delete headers['connection']
delete headers['accept']
delete headers['content-length']
res.end() // We don't need the incoming connection anymore
request({
  method: 'post',
  body: req.body,
  headers: headers,
  json: true,
  url: `http://myapi/${req.url}`
}, (err, _res, body)=>{
  if(err) return done(err);
  // Test my api response here as if Google sent it.
})

0

Benim durumum bu bir hata değil, krom tarayıcı için beklenen bir davranıştı. Chrome tls bağlantısını canlı tutar (sanırım hız için), ancak node.js sunucusu 2 dakika sonra durdurur ve bir hata alırsınız.

Uç tarayıcı kullanarak GET isteğini denerseniz, hiçbir hata olmaz. Krom pencereyi kapatırsanız - hemen hata alırsınız.

Peki ne yapmalı? 1) Bu hataları filtreleyebilirsiniz, çünkü bunlar gerçekten hata değildir. 2) Belki daha iyi bir çözüm var :)


0

Burada ek bir durum daha var, ki Electron "localhost" alan adının hayranı değil. Benim durumumda bunu değiştirmem gerekiyordu:

const backendApiHostUrl = "http://localhost:3000";

buna:

const backendApiHostUrl = "http://127.0.0.1:3000";

Bundan sonra sorun ortadan kalktı.

Bu, DNS çözümlemesinin (yerel veya uzak) bazı sorunlara neden olabileceği anlamına gelir.


0

Bana sadece farklı bir port numarasına geçiş için vb CORS'yi, kontrol düğümün js kodu, mongodb bağlantı dizesi, içine uzun düzeltiminden sonra server.listen(port);o içine, işe yapılan postman, bunu da deneyin. proxySadece varsayılan ayarlarda değişiklik yok .

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.