最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

nodejs的錯(cuò)誤處理過(guò)程記錄

 更新時(shí)間:2021年04月14日 09:58:32   作者:the gc  
這篇文章主要給大家介紹了關(guān)于nodejs的錯(cuò)誤處理過(guò)程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

本文以連接錯(cuò)誤ECONNREFUSED為例,看看nodejs對(duì)錯(cuò)誤處理的過(guò)程。 假設(shè)我們有以下代碼

1.  const net = require('net');  
2.  net.connect({port: 9999})

如果本機(jī)上沒(méi)有監(jiān)聽(tīng)9999端口,那么我們會(huì)得到以下輸出。

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)

我們簡(jiǎn)單看一下connect的調(diào)用流程。

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.  // 開(kāi)始三次握手建立連接  
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++層直接調(diào)用Libuv的uv_tcp_connect,并且設(shè)置回調(diào)是AfterConnect。接著我們看libuv的實(shí)現(xiàn)。

1.  do {  
2.      errno = 0;  
3.      // 非阻塞調(diào)用  
4.      r = connect(uv__stream_fd(handle), addr, addrlen);  
5.    } while (r == -1 && errno == EINTR);  
6.    // 連接錯(cuò)誤,判斷錯(cuò)誤碼  
7.    if (r == -1 && errno != 0) {  
8.      // 還在連接中,不是錯(cuò)誤,等待連接完成,事件變成可讀  
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,等待可寫(xiě)事件  
22.   handle->connect_req = req;  
23.   uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);

我們看到Libuv以異步的方式調(diào)用操作系統(tǒng),然后把request掛載到handle中,并且注冊(cè)等待可寫(xiě)事件,當(dāng)連接失敗的時(shí)候,就會(huì)執(zhí)行uv stream_io回調(diào),我們看一下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);

獲取錯(cuò)誤信息后回調(diào)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);

接著調(diào)用JS層的oncomplete回調(diào)。

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構(gòu)造錯(cuò)誤信息,然后銷毀socket并且以ex為參數(shù)觸發(fā)error事件。我們看看uvExceptionWithHostPort的實(shí)現(xiàn)。

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.   // 獲取調(diào)用棧信息但不包括當(dāng)前調(diào)用的函數(shù)uvExceptionWithHostPort,注入stack字段到ex中  
23.   Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort);  
24.   return ex;  
25. }

我們看到錯(cuò)誤信息主要通過(guò)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. }

繼續(xù)往下看,uvErrmapGet調(diào)用了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中獲取錯(cuò)誤信息  
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每個(gè)元素中的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. }

我們看到錯(cuò)誤信息存在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宏展開(kāi)后如下

1.  {UV_E2BIG, "E2BIG", "argument list too long"},  
2.  {UV_EACCES, "EACCES", "permission denied"},  
3.  {UV_EADDRINUSE, "EADDRINUSE", "address already in use"},  
4.  ……

所以導(dǎo)出到JS層的結(jié)果如下

1.  {  
2.    // 鍵是一個(gè)數(shù)字,由Libuv定義,其實(shí)是封裝了操作系統(tǒng)的定義
3.    UV_ECONNREFUSED: ["ECONNREFUSED", "connection refused"],    
4.    UV_ECONNRESET: ["ECONNRESET", "connection reset by peer"]   
5.    ...   
6.  }

Node.js最后會(huì)組裝這些信息返回給調(diào)用方。這就是我們輸出的錯(cuò)誤信息。那么為什么會(huì)是ECONNREFUSED呢?我們看一下操作系統(tǒng)對(duì)于該錯(cuò)誤碼的邏輯。

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

當(dāng)操作系統(tǒng)收到一個(gè)發(fā)給該socket的rst包的時(shí)候會(huì)執(zhí)行tcp_reset,我們看到當(dāng)socket處于發(fā)送syn包等待ack的時(shí)候,如果收到一個(gè)fin包,則會(huì)設(shè)置錯(cuò)誤碼為ECONNREFUSED。我們輸出的正是這個(gè)錯(cuò)誤碼。

總結(jié)

到此這篇關(guān)于nodejs的錯(cuò)誤處理過(guò)程記錄的文章就介紹到這了,更多相關(guān)nodejs錯(cuò)誤處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決await在forEach中不起作用的問(wèn)題

    解決await在forEach中不起作用的問(wèn)題

    這篇文章主要介紹了解決await在forEach中不起作用的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Nodejs新特性async和await的使用詳解

    Nodejs新特性async和await的使用詳解

    這篇文章主要介紹了Nodejs新特性async和await的使用,主要包括Es6常見(jiàn)語(yǔ)法的使用及Async、Await和Promise的示例代碼,需要的朋友可以參考下
    2022-09-09
  • 淺談手寫(xiě)node可讀流之流動(dòng)模式

    淺談手寫(xiě)node可讀流之流動(dòng)模式

    這篇文章主要介紹了淺談手寫(xiě)node可讀流之流動(dòng)模式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • nodejs中全局變量的實(shí)例解析

    nodejs中全局變量的實(shí)例解析

    本文主要介紹了nodejs中全局變量的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • Node.js net模塊的使用示例

    Node.js net模塊的使用示例

    本文主要介紹了Node.js net模塊的使用示例,net模塊支持TCP通信,處理TCP連接和數(shù)據(jù)傳輸,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-02-02
  • Node.js讀取和寫(xiě)入文件的代碼示例

    Node.js讀取和寫(xiě)入文件的代碼示例

    在現(xiàn)代前端開(kāi)發(fā)中,Node.js已經(jīng)成為不可或缺的一部分,其高效的非阻塞I/O模型使得它非常適合處理文件操作,在這篇博客中,我們將探討如何使用Node.js讀取和寫(xiě)入文件,我們會(huì)涉及基本的文件系統(tǒng)模塊,并提供可運(yùn)行的代碼示例,以便您可以輕松上手
    2024-11-11
  • nvm mac 環(huán)境搭建過(guò)程

    nvm mac 環(huán)境搭建過(guò)程

    這篇文章主要為大家介紹了nvm mac 環(huán)境搭建過(guò)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • node.js實(shí)現(xiàn)為PDF添加水印的示例代碼

    node.js實(shí)現(xiàn)為PDF添加水印的示例代碼

    這篇文章主要介紹了node.js實(shí)現(xiàn)為PDF添加水印的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • Windows上node.js的多版本管理工具用法實(shí)例分析

    Windows上node.js的多版本管理工具用法實(shí)例分析

    這篇文章主要介紹了Windows上node.js的多版本管理工具用法,結(jié)合實(shí)例形式分析了Windows平臺(tái)上node.js多版本管理工具nvm-windows的相關(guān)使用技巧,需要的朋友可以參考下
    2019-11-11
  • node.js接口復(fù)習(xí)及使用案例解析

    node.js接口復(fù)習(xí)及使用案例解析

    這篇文章主要為大家介紹了node.js接口的復(fù)習(xí)及使用案例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07

最新評(píng)論

三原县| 关岭| 高唐县| 房产| 铅山县| 肥城市| 乌兰县| 盐山县| 东至县| 定边县| 德令哈市| 新民市| 会泽县| 留坝县| 岑巩县| 治多县| 怀仁县| 股票| 榆树市| 皋兰县| 井研县| 沛县| 沂源县| 田林县| 安图县| 于田县| 平湖市| 奎屯市| 景德镇市| 秦安县| 黄平县| 丹东市| 五峰| 大庆市| 宁国市| 凤台县| 汉川市| 上饶市| 会东县| 琼结县| 金乡县|