NodeJS - Ổ cắm trên mạng treo lên trên thực tế có nghĩa là gì?


276

Tôi đang xây dựng một trình quét web với Node và Cheerio và đối với một trang web nhất định tôi đang gặp phải lỗi sau (nó chỉ xảy ra trên một trang web này, không có trang nào khác mà tôi cố gắng cạo.

Nó xảy ra ở một vị trí khác nhau mỗi lần, vì vậy đôi khi nó url xgây ra lỗi, lần khác url xlà tốt và đó là một url hoàn toàn khác:

    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)

Điều này rất khó để gỡ lỗi, tôi thực sự không biết bắt đầu từ đâu. Để bắt đầu, những gì một ổ cắm ngắt kết nối lỗi? Đó có phải là lỗi 404 hay tương tự không? Hay nó chỉ có nghĩa là máy chủ từ chối kết nối?

Tôi không thể tìm thấy một lời giải thích về điều này bất cứ nơi nào!

EDIT: Đây là một mẫu mã (đôi khi) trả về lỗ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
    });
}

Không có cuộc gọi trực tiếp để đóng kết nối, nhưng tôi đang sử dụng Node Request(theo như tôi có thể nói) sử dụng http.getvì vậy điều này là không bắt buộc, hãy sửa tôi nếu tôi sai!

EDIT 2: Đây là một đoạn mã đang sử dụng thực tế đang gây ra lỗi. prodURLvà các biến khác chủ yếu là các bộ chọn jquery được xác định trước đó. Cái này sử dụng asyncthư viện cho 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);
                }
            ]);
    });
}

26
Điều đó có nghĩa là ổ cắm không gửi endsự kiện kết nối trong khoảng thời gian chờ. Nếu bạn nhận được yêu cầu cho cổ vũ thông qua http.request(không http.get). Bạn phải gọi request.end()để hoàn thành việc gửi yêu cầu.
dùng568109

1
@ user568109 Tôi cần lưu ý, tôi đang sử dụng requestdịch vụ nút , không phải là một http.requestyêu cầu cụ thể (tôi nghĩ, tôi rất mới với nút!). Đây là một: github.com/mikeal/request Điều này có vẻ như nó hoàn thành yêu cầu tự động, phải không? EDIT: Theo các tài liệu, http method, defaults to GETvì vậy đó không phải là vấn đề.
JVG

2
Sau đó, nó không phải là vấn đề. Điều gì xảy ra nếu bạn nhận xét phần cào bao gồm cả Cheio.load và trả lại cùng một nội dung. Bắt ở đây là, cheerio.loadkhông đồng bộ. Vì vậy, nó có thể không hoàn thành trước khi bạn bắt đầu làm công cụ với $.
dùng568109

4
Đôi khi tôi cũng nhận thấy rằng nếu tôi thu thập dữ liệu một trang web quá mạnh mẽ (như hơn 10 kết nối đồng thời) thì họ sẽ bắt đầu phản hồi với việc treo ổ cắm, do đó cũng có thể như vậy.
tobek

1
Chỉ cần FYI, bằng tiếng Anh, hang up có nghĩa là kết thúc một cuộc trò chuyện điện tử bằng cách cắt kết nối ; bắt nguồn từ việc treo điện thoại kiểu cũ.
Toàn cảnh

Câu trả lời:


161

Có hai trường hợp khi socket hang upbị ném:

Khi bạn là khách hàng

Khi bạn, với tư cách là khách hàng, gửi yêu cầu đến máy chủ từ xa và không nhận được phản hồi kịp thời. Ổ cắm của bạn đã kết thúc mà ném lỗi này. Bạn nên nắm bắt lỗi này và quyết định cách xử lý nó: cho dù thử lại yêu cầu, xếp hàng cho lần sau, v.v.

Khi bạn là một máy chủ / proxy

Khi bạn, với tư cách là máy chủ, có lẽ là máy chủ proxy, nhận được yêu cầu từ máy khách, sau đó bắt đầu hành động theo yêu cầu đó (hoặc chuyển tiếp yêu cầu đến máy chủ ngược dòng) và trước khi bạn chuẩn bị phản hồi, máy khách quyết định hủy / hủy bỏ yêu cầu.

Dấu vết ngăn xếp này cho thấy những gì xảy ra khi khách hàng hủy yêu cầu.

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)

Dòng http.js:1526:9điểm với cùng socketCloseListenernêu trên bởi @Blender, cụ thể:

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

Đây là một trường hợp điển hình nếu máy khách là người dùng trong trình duyệt. Yêu cầu tải một số tài nguyên / trang mất nhiều thời gian và người dùng chỉ cần làm mới trang. Hành động như vậy làm cho yêu cầu trước đó bị hủy bỏ mà phía máy chủ của bạn đưa ra lỗi này.

Vì lỗi này là do mong muốn của khách hàng, họ không mong nhận được bất kỳ thông báo lỗi nào. Vì vậy, không cần phải coi lỗi này là nghiêm trọng. Chỉ cần bỏ qua nó. Điều này được khuyến khích bởi thực tế là trên lỗi như vậy, resổ cắm mà khách hàng của bạn đã nghe, mặc dù vẫn có thể ghi, bị phá hủy.

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

Vì vậy, không có điểm nào để gửi bất cứ thứ gì, ngoại trừ đóng rõ ràng đối tượng phản hồi:

res.end();

Tuy nhiên, điều bạn nên làm chắc chắn nếu bạn một máy chủ proxy đã chuyển yêu cầu lên thượng nguồn, là hủy bỏ yêu cầu nội bộ của bạn lên thượng nguồn, cho thấy sự thiếu quan tâm của bạn đối với phản hồi, từ đó sẽ cho biết ngược dòng máy chủ, có lẽ, dừng một hoạt động đắt tiền.


2
Làm thế nào tôi có thể, với tư cách là khách hàng, chỉ cần thực hiện yêu cầu chờ lâu hơn? Nó bị lỗi ở 35 giây và tôi cần nó đợi khoảng một phút.
Tiền lớn

Tôi đang đối mặt với vấn đề tương tự. Có thể đợi phản hồi và bắt đầu gửi yêu cầu tiếp theo như từng người thực hiện. Tôi có thể biết cách xử lý ổ cắm này bị treo không?.
Deepak

@BigMoney bạn có thể sử dụng setTimeout(). xem câu hỏi này: stackoverflow.com/questions/6214902/ Cách
holla

Thông tin chi tiết của bạn đã giúp tôi thoát khỏi địa ngục, tôi đang sử dụng node.js làm máy chủ proxy giữa máy chủ ngược dòng và máy khách, thời gian chờ theo yêu cầu đã ném Lỗi này chỉ vì tôi quên sử dụng res.send, cảm ơn
Farzad YZ

Bạn có thể nhận được "ổ cắm treo" với tư cách là khách hàng khi bạn đang cố gắng thực hiện yêu cầu thứ hai đến máy chủ web phát triển của Django qua cùng một kết nối. Nó không hỗ trợ keep-alive. Và trong trường hợp khách hàng của bạn mong đợi nó, bạn sẽ nhận được lỗi. Nó nhìn dọc theo các dòng sau .
x-yuri

53

Hãy xem nguồ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;
  }
}

Thông báo được phát ra khi máy chủ không bao giờ gửi phản hồi.


2
Từ góc độ chức năng, bạn có thể giải thích điều này có nghĩa là gì? Tôi đang cố gắng xây dựng các biện pháp bảo vệ ở đây bằng cách thêm các url vi phạm vào một mảng sau đó cạo chúng sau. Tôi đã đọc ở một vài nơi rằng các lỗi có thể là vấn đề xếp hàng với Node, không biết cách tốt nhất để khắc phục và tránh điều này.
JVG

5
Nhưng nó đợi bao lâu?
Dấu phẩy

2
Nó nên sử dụng en.wikipedia.org/wiki/Exponential_backoff để thực hiện "bao lâu".
Norman H

"ổ cắm treo" này là vô nghĩa. Đây chỉ là một bất ngờ từ nhóm nodejs.
puchu

45

Một trường hợp đáng nói: khi kết nối từ Node.js đến Node.js bằng Express, tôi nhận được "socket treo" nếu tôi không đặt tiền tố đường dẫn URL được yêu cầu bằng "/".


1
đó là vấn đề của tôi, cả máy khách và máy chủ trong http node.js thuần túy
ashley willis

1
@silentorb: Bạn có thể vui lòng hiển thị url ví dụ không? Tôi đang đối mặt với lỗi tương tự trong trường hợp này .. Cảm ơn.
Pritam

4
Lỗi: "người dùng / đăng nhập", Thành công: "/ người dùng / đăng nhập"
im lặng

4
Người đàn ông tôi đã dành gần một giờ để gỡ lỗi nó! Xem câu trả lời của bạn và nghĩ SH **, đã thêm / và nó hoạt động tốt :) cảm ơn!
Daniel Gruszchot

4
Bạn đã tiết kiệm cho tôi hàng giờ với phản ứng này!
imhotep

32

Tôi sử dụng require('http')để tiêu thụ https dịch vụ và nó cho thấy " socket hang up".

Sau đó, tôi thay đổi require('http')để require('https')thay thế, và nó đang hoạt động.


Mặc dù đây có thể là một giải pháp cho vấn đề, nhưng nó không phải là một câu trả lời cho câu hỏi. Người đăng muốn có câu trả lời về ý nghĩa của thông báo lỗi. Ngoài ra, có nhiều câu trả lời chất lượng cao. Bạn không cung cấp giá trị bổ sung.
Julian Dorn

19
Cảm ơn bạn đã bình luận. Tôi lãng phí thời gian cho lỗi này. Cuối cùng, tôi chỉ cần thử giải pháp này và nó hoạt động. Chỉ muốn chia sẻ. Hy vọng nó hữu ích cho người khác để không lãng phí thời gian của họ, không phải để ca ngợi như câu trả lời chất lượng cao.
Aekkawit Chanpen

12
@JohannesDorn Đây là một câu trả lời ngầm cho câu hỏi về lỗi có nghĩa là gì. Và một điều hữu ích ở đó.
Ulad Kasach

29

dưới đây là một ví dụ đơn giản khi tôi gặp lỗi tương tự khi tôi bỏ lỡ để thêm mã nhận xét vào ví dụ bên dưới. Uncomment mã req.end()sẽ giải quyết vấn đề này.

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
Điều này đã cứu sự tỉnh táo của tôi ... Cảm ơn!
PGallagher

Bạn là một anh hùng! Cảm ơn bạn.
Xenhat

17

Mở rộng câu trả lời của Blender, điều này xảy ra trong một số tình huống. Những cái phổ biến nhất tôi gặp phải là:

  1. Máy chủ bị sập.
  2. Các máy chủ từ chối kết nối của bạn, rất có thể bị chặn bởi User-Agent.

socketCloseListener, như được nêu trong câu trả lời của Blender, không phải là nơi duy nhất có lỗi treo máy được tạo ra.

Ví dụ, được tìm thấy ở đây :

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

Bạn có thể thử curlvới các tiêu đề và những thứ đang được gửi từ Node và xem bạn có nhận được phản hồi ở đó không. Nếu bạn không nhận được phản hồi curl, nhưng bạn nhận được phản hồi trong trình duyệt của mình, thì User-Agentrất có thể tiêu đề của bạn sẽ bị chặn.


3
Một lý do khác khiến máy chủ có thể từ chối kết nối của bạn (tôi chỉ nhấn vào điều này khi chuyển sang prod thay vì QA), là nếu máy chủ của bạn đang mong đợi một yêu cầu https thay vì http.
mcole

7

Một trường hợp đáng nói khác (đối với Linux và OS X) là nếu bạn sử dụng thư viện như httpsđể thực hiện các yêu cầu hoặc nếu bạn chuyển https://...như một URL của phiên bản được phục vụ cục bộ, bạn sẽ sử dụng cổng 443là cổng riêng dành riêng và bạn có thể kết thúc bằng Socket hang uphoặc ECONNREFUSEDlỗi.

Thay vào đó, sử dụng cổng 3000, fe và thực hiện một httpyêu cầu.


6

Tôi gặp vấn đề tương tự khi sử dụng thư viện Nano để kết nối với Couch DB . Tôi đã cố gắng tinh chỉnh kết nối nhóm với việc sử dụng thư viện keepaliveagent và nó liên tục thất bại với thông báo treo ổ cắm .

var KeepAliveAgent = require('agentkeepalive');

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

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

Sau một vài lần vật lộn, tôi đã có thể khắc phục được vấn đề - vì nó xuất hiện nên đó là một lỗi rất đơn giản. Tôi đã kết nối với cơ sở dữ liệu thông qua giao thức HTTPS, nhưng tôi vẫn truyền cho đối tượng nano của mình một tác nhân lưu giữ được tạo như các ví dụ để sử dụng chương trình thư viện này (họ dựa vào một số mặc định sử dụng http).

Một thay đổi đơn giản để sử dụng HttpsAgent đã thực hiện thủ thuật:

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

1
Để biết thêm một chút chi tiết, nếu yêu cầu được cấu hình cho cổng 443 và yêu cầu được đưa ra thông qua mô-đun http chứ không phải mô-đun https, thì bạn sẽ có một ổ cắm bị treo. Sẽ thật tuyệt nếu có thêm chi tiết về lý do tại sao việc ngắt kết nối xảy ra (đàm phán SSL / TLS?). Tôi đã thấy mức độ chi tiết trong ASP.NET chẳng hạn.
Richard Collette

6

Điều này gây ra cho tôi các vấn đề, vì tôi đang làm mọi thứ được liệt kê ở đây, nhưng vẫn bị lỗi. Nó chỉ ra rằng việc gọi req.abort () thực sự gây ra lỗi, với mã ECONNRESET, vì vậy bạn thực sự phải nắm bắt điều đó trong trình xử lý lỗi của mình.

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

5

Đối với requestngười dùng mô-đun

Hết giờ

Có hai loại thời gian chờ chính: thời gian chờ kết nốithời gian chờ đọc . Một thời gian chờ kết nối xảy ra nếu thời gian chờ là hit trong khi khách hàng của bạn đang cố gắng thiết lập một kết nối đến một máy từ xa (tương ứng với các connect()cuộc gọi trên socket). Thời gian chờ đọc xảy ra bất cứ khi nào máy chủ quá chậm để gửi lại một phần của phản hồi.

Lưu ý rằng thời gian chờ kết nối phát ra ETIMEDOUTlỗi và đọc hết thời gian chờ phát ra ECONNRESETlỗi.


3

Tôi đã có cùng một vấn đề trong khi yêu cầu một số máy chủ. Trong trường hợp của tôi, việc đặt bất kỳ giá trị nào cho Tác nhân người dùng trong các tiêu đề trong tùy chọn yêu cầu đã giúp tôi.

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

Đây không phải là trường hợp chung và phụ thuộc vào cài đặt máy chủ.


2

Ngoài ra lý do có thể là do sử dụng appví dụ expressthay vì servertừconst server = http.createServer(app) trong khi tạo ổ cắm máy chủ.

Sai lầm

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

Chính xác

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

Tôi thực hiện cả phát triển web (nút) và Android, và mở trình mô phỏng và docker thiết bị Android Studio cùng nhau, cả hai đều sử dụng cổng 8601, nó phàn nàn socket hang uplỗi, sau khi đóng trình giả lập thiết bị Android Studio và nó hoạt động tốt ở phía nút. Không sử dụng trình giả lập thiết bị Android Docker và docker cùng nhau.


1

Tôi đã gặp một lỗi tương tự khi sử dụng CouchDB trên cụm OCP.

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

Phải là "http", không phải "https", để kết nối với cá thể CouchDB của tôi. Hy vọng nó có thể hữu ích cho bất cứ ai phải đối mặt với vấn đề tương tự.


0

Trong trường hợp của tôi, đó là do phản hồi của ứng dụng / json được định dạng sai (chứa dấu vết ngăn xếp). Phản hồi không bao giờ được gửi đến máy chủ. Điều đó rất khó để gỡ lỗi bởi vì, không có nhật ký. Chủ đề này giúp tôi rất nhiều để hiểu những gì xảy ra.


0

Trong trường hợp bạn đang sử dụng proxy-http-proxy, vui lòng lưu ý vấn đề này, điều này sẽ dẫn đến lỗi treo ổ cắm: https://github.com/nodejitsu/node-http-proxy/issues/180 .

Để giải quyết, cũng trong liên kết này, chỉ cần di chuyển khai báo tuyến API (để ủy quyền) trong các tuyến tốc hành trước express.bodyParser ().


0

Gặp vấn đề này ngày hôm qua khi chạy ứng dụng web và máy chủ node.js của tôi thông qua IntelliJ IDEA 2016.3.6. Tất cả những gì tôi phải làm là xóa cookie và bộ nhớ cache trong trình duyệt Chrome của mình.


0

Nếu bạn gặp phải lỗi này qua kết nối https và nó xảy ra ngay lập tức thì đó có thể là sự cố khi thiết lập kết nối SSL.

Đối với tôi đó là vấn đề này https://github.com/nodejs/node/issues/9845 nhưng đối với bạn nó có thể là một cái gì đó khác. Nếu đó là một vấn đề với ssl thì bạn sẽ có thể sao chép nó với gói tls / ssl của nodejs chỉ cố gắng kết nối với miền


0

Tôi nghĩ đáng chú ý ...

Tôi đã tạo các thử nghiệm cho Google API. Tôi đã chặn yêu cầu với một máy chủ tạm thời, sau đó chuyển tiếp chúng đến api thật. Tôi đã cố gắng để vượt qua các tiêu đề trong yêu cầu, nhưng một vài tiêu đề đã gây ra vấn đề với express ở đầu bên kia.

Cụ thể, tôi đã phải xóa connection, acceptcontent-lengthtiêu đề trước khi sử dụng các module theo yêu cầu để chuyển tiếp theo.

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

Tôi là trường hợp của tôi, đó không phải là lỗi, nhưng là hành vi dự kiến ​​cho trình duyệt chrome. Chrome giữ kết nối tls sống (đối với tốc độ tôi nghĩ), nhưng máy chủ node.js dừng kết nối đó sau 2 phút và bạn gặp lỗi.

Nếu bạn thử yêu cầu GET bằng trình duyệt biên, sẽ không có lỗi nào cả. Nếu bạn sẽ đóng cửa sổ chrome - bạn sẽ gặp lỗi ngay lập tức.

Vậy lam gi? 1) Bạn có thể lọc lỗi này, vì chúng không thực sự là lỗi. 2) Có lẽ có một giải pháp tốt hơn :)


0

Dường như có một trường hợp bổ sung ở đây, đó là Electron không phải là một fan hâm mộ của tên miền "localhost". Trong trường hợp của tôi, tôi cần phải thay đổi điều này:

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

đến đây:

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

Sau đó, vấn đề chỉ cần biến mất.

Điều này có nghĩa là độ phân giải DNS (cục bộ hoặc từ xa) cũng có thể gây ra một số vấn đề.


0

Sau một thời gian dài gỡ lỗi thành mã js nút, chuỗi kết nối mongodb, kiểm tra CORS, v.v. Đối với tôi, chỉ cần chuyển sang một số cổng khác server.listen(port);đã khiến nó hoạt động, vào postman, hãy thử điều đó. Không có thay đổi cho proxycác cài đặt chỉ mặc định.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.