首页前端开发JavaScriptnodejs的错误处理过程记录

nodejs的错误处理过程记录

时间2024-02-01 10:34:03发布访客分类JavaScript浏览673
导读:收集整理的这篇文章主要介绍了nodejs的错误处理过程记录,觉得挺不错的,现在分享给大家,也给大家做个参考。 本文以连接错误ECONNREFUSED为例,看看nodejs对错误处理的过程...
收集整理的这篇文章主要介绍了nodejs的错误处理过程记录,觉得挺不错的,现在分享给大家,也给大家做个参考。

本文以连接错误ECONNREFUSED为例,看看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)

我们简单看一下connect的调用流程。

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_castconst 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以异步的方式调用操作系统,然后把request挂载到handle中,并且注册等待可写事件,当连接失败的时候,就会执行uv stream_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.  LocalValue>
 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);
    

exceptionWithHostPort构造错误信息,然后销毁socket并且以ex为参数触发error事件。我们看看uvExceptionWithHostPort的实现。

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 FunctionCallbackInfoValue>
    &
 args) {
      2.    Environment* env = Environment::Getcurrent(args);
      3.    Isolate* isolate = env->
    isolate();
      4.    LocalContext>
     context = env->
    context();
      5.    6.    LocalMap>
     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和message12.     const auto&
     error = per_process::uv_errors_map[i];
      13.     LocalValue>
 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_map的定义。

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_MAP宏展开后如下

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最后会组装这些信息返回给调用方。这就是我们输出的错误信息。那么为什么会是ECONNREFUSED呢?我们看一下操作系统对于该错误码的逻辑。

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错误处理内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:
  • NodeJs内存占用过高的排查实战记录
  • 详解使用Nodejs内置加密模块实现对等加密与解密
  • 详解nodejs中的异步迭代器
  • 详解nodejs内置模块
  • nodejs模块系统源码分析
  • 浅谈JS和Nodejs中的事件驱动
  • 如何在Nodejs中使用模块fs文件系统
  • nodejs代码执行绕过的一些技巧汇总
  • Nodejs探秘之深入理解单线程实现高并发原理
  • 如何用nodejs给C#写一个数据表的实体类生成工具

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

上一篇: 如何用CocosCreator实现射击小游...下一篇:带你彻底理解JavaScript中的原型...猜你在找的JavaScript相关文章 html font标签如何设置字体大小?html font标签属性用法介绍2022-05-16vue3+TypeScript+vue-router的使用方法2022-04-16vue3获取当前路由地址2022-04-16如何利用React实现图片识别App2022-04-16JavaScript展开运算符和剩余运算符的区别详解2022-04-16微信小程序中使用vant框架的具体步骤2022-04-16Vue elementUI表单嵌套表格并对每行进行校验详解2022-04-16如何利用Typescript封装本地存储2022-04-16微信小程序中wxs文件的一些妙用分享2022-04-16JavaScript的Set数据结构详解2022-04-16 其他相关热搜词更多phpjavapython程序员loadpost-format-gallery

若转载请注明出处: nodejs的错误处理过程记录
本文地址: https://pptw.com/jishu/595286.html
ToString()使用方法汇总(c#) div弹出层的ajax登录(Jquery版+c#)

游客 回复需填写必要信息