NodeJS - что означает "сотовый вешать трубку"?

Я создаю веб-скребок с Node и Cheerio, и для определенного веб-сайта я получаю следующую ошибку (это происходит только на этом веб-сайте, другие, которые я пытаюсь очистить).

Это происходит в другом месте каждый раз, поэтому иногда он url x, который выдает ошибку, в других случаях url x отлично, и он совсем другой URL:

    Error!: Error: socket hang up using [insert random URL, it 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)

Это очень сложно отлаживать, я не знаю, с чего начать. Для начала, какая IS ошибка зависания сокета? Это ошибка 404 или аналогичная? Или это просто означает, что сервер отказался от соединения?

Я не могу найти объяснения этого нигде!

EDIT: Здесь образец кода, который (иногда) возвращает ошибки:

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

Нет прямого вызова для закрытия соединения, но я использую Node Request, который (насколько я могу судить) использует http.get так что это не требуется, исправьте меня, если я ошибаюсь!

РЕДАКТИРОВАТЬ 2: Здесь фактический, используемый бит кода, вызывающий ошибки. prodURL и другие переменные - это в основном селектор jquery, которые определены ранее. Это использует библиотеку async для Node.

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);
                }
            ]);
    });
}

Ответ 1

Есть два случая, когда бросается socket hang up:

Когда вы являетесь клиентом

Когда вы, как клиент, отправляете запрос на удаленный сервер и не получаете своевременного ответа. Ваш сокет завершен, который выдает эту ошибку. Вы должны поймать эту ошибку и решить, как ее обрабатывать: повторите попытку запроса, поставите ее на потом и т.д.

Когда вы являетесь сервером/прокси

Когда вы, как сервер, возможно, прокси-сервер, получаете запрос от клиента, затем начинаете действовать на него (или ретранслируете запрос на восходящий сервер), и до того, как вы подготовили ответ, клиент решает отменить/прервать запрос.

Эта трассировка стека показывает, что происходит, когда клиент отменяет запрос.

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)

Линия http.js:1526:9 указывает на тот же socketCloseListener, упомянутый выше @Blender, в частности:

// 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;
}

Это типичный случай, если клиент является пользователем в браузере. Запрос на загрузку некоторого ресурса/страницы занимает много времени, и пользователи просто обновляют страницу. Такое действие приводит к тому, что предыдущий запрос прерывается, что на вашей стороне сервера вызывает эту ошибку.

Поскольку эта ошибка вызвана желанием клиента, они не ожидают получить сообщение об ошибке. Поэтому нет необходимости рассматривать эту ошибку как критическую. Просто проигнорируйте это. Этому способствует тот факт, что при такой ошибке сокет res, который прослушивал ваш клиент, хотя и недоступен для записи, был уничтожен.

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

Итак, нет смысла отправлять что-либо, кроме явно закрывающего объекта ответа:

res.end();

Однако то, что вы должны делать наверняка, если вы являетесь прокси-сервером, который уже передал запрос на восходящий поток, заключается в том, чтобы прервать ваш внутренний запрос вверх по течению, указывая на то, что вы не заинтересованы в ответ, который, в свою очередь, скажет серверу вверх, возможно, прекратить дорогостоящую операцию.

Ответ 2

Посмотрите источник:

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;
  }
}

Сообщение выдается, когда сервер никогда не отправляет ответ.

Ответ 3

В одном случае стоит упомянуть: при подключении с Node.js к Node.js с помощью Express я получаю "зависание сокета", если я не префикс запрашиваемого URL-адреса с помощью "/".

Ответ 4

Я использовал require('http') чтобы потреблять https- сервис, и он показал, что " socked hang up ".

Затем я использую require('https') вместо этого, и он работает.

Ответ 5

Развернувшись на ответ Blender, это происходит в ряде ситуаций. Наиболее распространенными из которых я сталкивался:

  • Сервер разбился.
  • Сервер отказался от вашего соединения, скорее всего заблокированного User-Agent.

socketCloseListener, как указано в ответе Blender, не является единственным местом, где создаются ошибки зависания.

Например, найдено здесь:

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

Вы можете попробовать curl с заголовками и отправить их из Node и посмотреть, есть ли у вас ответ. Если вы не получите ответ с помощью curl, но вы получите ответ в своем браузере, ваш заголовок User-Agent, скорее всего, будет заблокирован.

Ответ 6

ниже - простой пример, когда я получил ту же ошибку, когда я пропустил, чтобы добавить комментарий в приведенном ниже примере. req.end() кода req.end() решит эту проблему.

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

Ответ 7

Еще один случай, который стоит упомянуть (для Linux и OS X), заключается в том, что если вы используете библиотеку типа https для выполнения запросов или передаете https://... в качестве URL-адреса экземпляра, используемого локально, вы будете использовать port 443, который является зарезервированным частным портом, и вы можете оказаться в ошибках Socket hang up или ECONNREFUSED.

Вместо этого используйте порт 3000, f.e. и выполните запрос http.

Ответ 8

У меня была та же проблема при использовании библиотеки Nano для подключения к Couch DB. Я попытался точно настроить объединение пула с использованием библиотеки keepaliveagent, и он продолжал терпеть неудачу с сообщением о разворачивании сокета.

var KeepAliveAgent = require('agentkeepalive');

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

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

После некоторых боев я смог прибить проблему - так как это получилось, это была очень простая ошибка. Я подключался к базе данных по протоколу HTTPS, но я продолжал передавать свой нано-объект агенту keepalive, созданному в качестве примеров для использования этого представления библиотеки (они полагаются на некоторые значения по умолчанию, которые используют http).

Одно простое изменение использования HttpsAgent сделало трюк:

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

Ответ 9

У меня была такая же проблема во время запроса на каком-то сервере. В моем случае мне удалось настроить любое значение User-Agent в заголовках в параметрах запроса.

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

Это не общий случай и зависит от настроек сервера.

Ответ 10

Для пользователей модуля request

Таймауты

Существует два основных типа тайм-аутов: таймауты подключения и таймауты чтения. Тайм-аут соединения возникает, если таймаут ударяется, пока ваш клиент пытается установить соединение с удаленным компьютером (соответствующий вызову connect() в сокете). Тайм-аут чтения возникает в любое время, когда сервер слишком медленный, чтобы отправить часть ответа.

Обратите внимание, что таймауты подключения ETIMEDOUT ошибку ETIMEDOUT, а таймауты чтения испускают ошибку ECONNRESET.

Ответ 11

Также причиной может быть использование экземпляра app express вместо server из const server = http.createServer(app) при создании серверного сокета.

Неправильно

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

Правильный

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

Ответ 12

Я занимаюсь разработкой как веб-сайтов, так и Android, а также открываю имитатор устройств Android Studio и докеров, оба из них используют порт 8601, он жаловался socket hang up ошибку socket hang up, после закрытия симулятора Android Studio, и он хорошо работает на стороне узла. Не используйте симулятор устройства Android Studio и докер.

Ответ 13

В моем случае это произошло потому, что ответ приложения /json был сильно отформатирован (содержит трассировку стека). Ответ никогда не отправлялся на сервер. Это было очень сложно отладить, потому что журнала не было. Этот поток помогает мне понять, что происходит.

Ответ 14

Если вы используете node -http-proxy, будьте внимательны к этой проблеме, что приведет к ошибке зависания сокета: https://github.com/nodejitsu/node-http-proxy/issues/180.

Для разрешения также в этой ссылке просто переместите объявление маршрута API (для проксирования) в пределах экспресс-маршрутов до express.bodyParser().

Ответ 15

В этом выпуске появилась вчерашняя версия моего веб-приложения и node.js-сервера через IntelliJ IDEA 2016.3.6. Все, что мне нужно было сделать, это очистить файлы cookie и кеш в браузере Chrome.

Ответ 16

Если вы испытываете эту ошибку по подключению https, и это происходит мгновенно, может возникнуть проблема с настройкой SSL-соединения.

Для меня это была проблема https://github.com/nodejs/node/issues/9845, но для вас это могло быть чем-то другим. Если это проблема с ssl, тогда вы должны иметь возможность воспроизвести его с помощью пакета nodejs tls/ssl, просто пытающегося подключиться к домену

Ответ 17

Я думаю, стоит отметить...

Я создавал тесты для API Google. Я перехватил запрос с временным сервером, а затем перенаправил их на настоящую апи. Я пытался просто пройти по заголовкам в запросе, но несколько заголовков вызывали проблему с выражением на другом конце.

А именно, я должен был удалить заголовки connection, accept и content-length прежде чем использовать модуль запроса для пересылки.

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.
})

Ответ 18

Это вызвало у меня проблемы, поскольку я делал все, перечисленные здесь, но все еще получал ошибки. Оказывается, вызов req.abort() на самом деле вызывает ошибку с кодом ECONNRESET, поэтому вам действительно нужно поймать это в обработчике ошибок.

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