nodejs의 오류 처리 과정 기록
1. const net = require('net');
2. net.connect({port: 9999})
만약 이 컴퓨터에 9999 포트를 감청하지 않았다면, 우리는 아래의 출력을 얻을 수 있을 것이다.
1. events.js:170
2. throw er; // Unhandled 'error' event
3. ^
4.
5. Error: connect ECONNREFUSED 127.0.0.1:9999
6. at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1088:14)
7. Emitted 'error' event at:
8. at emitErrorNT (internal/streams/destroy.js:91:8)
9. at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
10. at processTicksAndRejections (internal/process/task_queues.js:81:17)
연결의 호출 절차를 간단히 봅시다.
1. const req = new TCPConnectWrap();
2. req.oncomplete = afterConnect;
3. req.address = address;
4. req.port = port;
5. req.localAddress = localAddress;
6. req.localPort = localPort;
7. //
8. err = self._handle.connect(req, address, port);
이어서 C++층connect의 논리를 보도록 하겠습니다.
1. err = req_wrap->Dispatch(uv_tcp_connect,
2. &wrap->handle_,
3. reinterpret_cast<const sockaddr*>(&addr),
4. AfterConnect);
C++ 레이어에서 Libuv의 uv_ 직접 호출tcp_connect, 콜백 설정은 AfterConnect입니다.이어서 우리는 libuv의 실현을 보았다.
1. do {
2. errno = 0;
3. //
4. r = connect(uv__stream_fd(handle), addr, addrlen);
5. } while (r == -1 && errno == EINTR);
6. // ,
7. if (r == -1 && errno != 0) {
8. // , , ,
9. if (errno == EINPROGRESS)
10. ; /* not an error */
11. else if (errno == ECONNREFUSED)
12. //
13. handle->delayed_error = UV__ERR(ECONNREFUSED);
14. else
15. return UV__ERR(errno);
16. }
17. uv__req_init(handle->loop, req, UV_CONNECT);
18. req->cb = cb;
19. req->handle = (uv_stream_t*) handle;
20. QUEUE_INIT(&req->queue);
21. // handle,
22. handle->connect_req = req;
23. uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
Libuv가 비동기적으로 운영체제를 호출하고 리퀘스트를 handle에 마운트하고 쓰기 가능한 이벤트를 등록하는 것을 보았습니다. 연결이 실패할 때 uvstream_io 콜백, Libuv 처리(uv stream_io)를 살펴보겠습니다.
1. getsockopt(uv__stream_fd(stream),
2. SOL_SOCKET,
3. SO_ERROR,
4. &error,
5. &errorsize);
6. error = UV__ERR(error);
7. if (req->cb)
8. req->cb(req, error);
오류 정보를 가져온 후 C++ 레이어에 대한 AfterConnect를 콜백합니다.
1. Local<Value> argv[5] = {
2. Integer::New(env->isolate(), status),
3. wrap->object(),
4. req_wrap->object(),
5. Boolean::New(env->isolate(), readable),
6. Boolean::New(env->isolate(), writable)
7. };
8.
9. req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);
이어서 JS 레이어의 oncomplete 리셋을 호출합니다.
1. const ex = exceptionWithHostPort(status,
2. 'connect',
3. req.address,
4. req.port,
5. details);
6. if (details) {
7. ex.localAddress = req.localAddress;
8. ex.localPort = req.localPort;
9. }
10. // socket
11. self.destroy(ex);
exception With HostPort 구조 오류 정보를 삭제한 다음 socket을 삭제하고 ex를 매개 변수로 error 이벤트를 터치합니다.uvException With HostPort의 구현을 살펴보겠습니다.
1. function uvExceptionWithHostPort(err, syscall, address, port) {
2. const [ code, uvmsg ] = uvErrmapGet(err) || uvUnmappedError;
3. const message = `${syscall} $[code]: ${uvmsg}`;
4. let details = '';
5.
6. if (port && port > 0) {
7. details = ` ${address}:${port}`;
8. } else if (address) {
9. details = ` ${address}`;
10. }
11. const tmpLimit = Error.stackTraceLimit;
12. Error.stackTraceLimit = 0;
13. const ex = new Error(`${message}${details}`);
14. Error.stackTraceLimit = tmpLimit;
15. ex.code = code;
16. ex.errno = err;
17. ex.syscall = syscall;
18. ex.address = address;
19. if (port) {
20. ex.port = port;
21. }
22. // uvExceptionWithHostPort, stack ex
23. Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort);
24. return ex;
25. }
우리는 오류 정보를 주로 uvErrmapGet을 통해 얻는 것을 보았다
1. function uvErrmapGet(name) {
2. uvBinding = lazyUv();
3. if (!uvBinding.errmap) {
4. uvBinding.errmap = uvBinding.getErrorMap();
5. }
6. return uvBinding.errmap.get(name);
7. }
8.
9. function lazyUv() {
10. if (!uvBinding) {
11. uvBinding = internalBinding('uv');
12. }
13. return uvBinding;
14. }
계속 아래를 보면 uvErrmapGet은 C++ 층의 uv 모듈의 getErrorMap을 호출합니다.
1. void GetErrMap(const FunctionCallbackInfo<Value>& args) {
2. Environment* env = Environment::GetCurrent(args);
3. Isolate* isolate = env->isolate();
4. Local<Context> context = env->context();
5.
6. Local<Map> err_map = Map::New(isolate);
7. // per_process::uv_errors_map
8. size_t errors_len = arraysize(per_process::uv_errors_map);
9. //
10. for (size_t i = 0; i < errors_len; ++i) {
11. // map uv_errors_map value, name message
12. const auto& error = per_process::uv_errors_map[i];
13. Local<Value> arr[] = {OneByteString(isolate, error.name),
14. OneByteString(isolate, error.message)};
15. if (err_map
16. ->Set(context,
17. Integer::New(isolate, error.value),
18. Array::New(isolate, arr, arraysize(arr)))
19. .IsEmpty()) {
20. return;
21. }
22. }
23.
24. args.GetReturnValue().Set(err_map);
25. }
오류 정보가 존재하는 것을 보았습니다per_process::uv_errors_map에서 uv_errors_맵의 정의입니다.
1. struct UVError {
2. int value;
3. const char* name;
4. const char* message;
5. };
6.
7. static const struct UVError uv_errors_map[] = {
8. #define V(name, message) {UV_##name, #name, message},
9. UV_ERRNO_MAP(V)
10. #undef V
11. };
UV_ERRNO_맵 매크로가 확장되면 다음과 같습니다.
1. {UV_E2BIG, "E2BIG", "argument list too long"},
2. {UV_EACCES, "EACCES", "permission denied"},
3. {UV_EADDRINUSE, "EADDRINUSE", "address already in use"},
4. ……
그래서 JS 레이어로 내보낸 결과는 다음과 같습니다.
1. {
2. // , Libuv ,
3. UV_ECONNREFUSED: ["ECONNREFUSED", "connection refused"],
4. UV_ECONNRESET: ["ECONNRESET", "connection reset by peer"]
5. ...
6. }
Node.js는 마지막에 이 정보를 조립하여 호출자에게 되돌려 줍니다.이것이 바로 우리가 출력한 잘못된 정보다.그럼 왜 ECONN REFUSED일까요?우리는 운영체제가 이 오류 코드에 대한 논리를 좀 봅시다.
1. static void tcp_reset(struct sock *sk)
2. {
3. switch (sk->sk_state) {
4. case TCP_SYN_SENT:
5. sk->sk_err = ECONNREFUSED;
6. break;
7. // ...
8. }
9.
10. }
운영체제가 이 socket에 보내진rst 패키지를 받았을 때 tcp_reset, 우리는 socket이syn 패키지를 보내서ack를 기다리고 있을 때fin 패키지를 받으면 ECONNREFUSED로 오류 코드를 설정하는 것을 보았습니다.우리가 출력한 것이 바로 이 오류 코드다.총결산
이 nodejs의 오류 처리 과정에 대한 기록을 소개합니다. 더 많은 nodejs 오류 처리 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Node.js를 AWS서버에서 사용하는 실습간단한 예제와 함께 AWS에서 Node.js를사용하는 법을 배워보도록 하겠다. 해당 github에 있는 레포지토리로 사용을 할 것이다. 3000번 포트로 Listen되는 예제이고 간단히 GET, POST, DELET...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.