一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - node.js - nodejs處理tcp連接的核心流程

nodejs處理tcp連接的核心流程

2022-01-24 16:46nodejs教程網 node.js

這篇文章主要介紹了nodejs處理tcp連接的核心流程,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

前幾天和一個小伙伴交流了一下nodejs中epoll和處理請求的一些知識,今天簡單來聊一下nodejs處理請求的邏輯。我們從listen函數開始。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
 // 設置處理的請求的策略,見下面的分析
 if (single_accept == -1) {
  const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
  single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */
 }
 if (single_accept)
  tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
 // 執行bind或設置標記
 err = maybe_new_socket(tcp, AF_INET, flags);
 // 開始監聽
 if (listen(tcp->io_watcher.fd, backlog))
  return UV__ERR(errno);
 // 設置回調
 tcp->connection_cb = cb;
 tcp->flags |= UV_HANDLE_BOUND;
 // 設置io觀察者的回調,由epoll監聽到連接到來時執行
 tcp->io_watcher.cb = uv__server_io;
 // 插入觀察者隊列,這時候還沒有增加到epoll,poll io階段再遍歷觀察者隊列進行處理(epoll_ctl)
 uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
 
 return 0;
}

我們看到,當我們createServer的時候,到Libuv層就是傳統的網絡編程的邏輯。這時候我們的服務就啟動了。在poll io階段,我們的監聽型的文件描述符和上下文(感興趣的事件、回調等)就會注冊到epoll中。正常來說就阻塞在epoll。那么這時候有一個tcp連接到來,會怎樣呢?epoll首先遍歷觸發了事件的fd,然后執行fd上下文中的回調,即uvserver_io。我們看看uvserver_io。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
 // 循環處理,uv__stream_fd(stream)為服務器對應的fd
 while (uv__stream_fd(stream) != -1) {
  // 通過accept拿到和客戶端通信的fd,我們看到這個fd和服務器的fd是不一樣的
  err = uv__accept(uv__stream_fd(stream));
  // uv__stream_fd(stream)對應的fd是非阻塞的,返回這個錯說明沒有連接可用accept了,直接返回
  if (err < 0) {
   if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
    return;
  }
  // 記錄下來
  stream->accepted_fd = err;
  // 執行回調
  stream->connection_cb(stream, 0);
  /*
   stream->accepted_fd為-1說明在回調connection_cb里已經消費了accepted_fd,
   否則先注銷服務器在epoll中的fd的讀事件,等待消費后再注冊,即不再處理請求了
  */
  if (stream->accepted_fd != -1) {
   uv__io_stop(loop, &stream->io_watcher, POLLIN);
   return;
  }
 /*
   ok,accepted_fd已經被消費了,我們是否還要繼續accept新的fd,
   如果設置了UV_HANDLE_TCP_SINGLE_ACCEPT,表示每次只處理一個連接,然后
   睡眠一會,給機會給其他進程accept(多進程架構時)。如果不是多進程架構,又設置這個,
   就會導致處理連接被延遲了一下
 */
  if (stream->type == UV_TCP &&
    (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
   struct timespec timeout = { 0, 1 };
   nanosleep(&timeout, NULL);
  }
 }
}

從uv__server_io,我們知道Libuv在一個循環中不斷accept新的fd,然后執行回調,正常來說,回調會消費fd,如此循環,直到沒有連接可處理了。接下來,我們重點看看回調里是如何消費fd的,大量的循環會不會消耗過多時間導致Libuv的事件循環被阻塞一會。tcp的回調是c++層的OnConnection。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 有連接時觸發的回調
template <typename WrapType, typename UVType>
void ConnectionWrap<WrapType, UVType>::OnConnection(uv_stream_t* handle,
                          int status) {
 // 拿到Libuv結構體對應的c++層對象                         
 WrapType* wrap_data = static_cast<WrapType*>(handle->data);
 CHECK_EQ(&wrap_data->handle_, reinterpret_cast<UVType*>(handle));
 
 Environment* env = wrap_data->env();
 HandleScope handle_scope(env->isolate());
 Context::Scope context_scope(env->context());
 
 // 和客戶端通信的對象
 Local<Value> client_handle;
 
 if (status == 0) {
  // Instantiate the client javascript object and handle.
  // 新建一個js層使用對象
  Local<Object> client_obj;
  if (!WrapType::Instantiate(env, wrap_data, WrapType::SOCKET)
       .ToLocal(&client_obj))
   return;
 
  // Unwrap the client javascript object.
  WrapType* wrap;
  // 把js層使用的對象client_obj所對應的c++層對象存到wrap中
  ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj);
  // 拿到對應的handle
  uv_stream_t* client = reinterpret_cast<uv_stream_t*>(&wrap->handle_);
  // 從handleaccpet到的fd中拿一個保存到client,client就可以和客戶端通信了
  if (uv_accept(handle, client))
   return;
  client_handle = client_obj;
 } else {
  client_handle = Undefined(env->isolate());
 }
 // 回調js,client_handle相當于在js層執行new TCP
 Local<Value> argv[] = { Integer::New(env->isolate(), status), client_handle };
 wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv);
}

代碼看起來很復雜,我們只需要關注uv_accept。uv_accept的參數,第一個是服務器對應的handle,第二個是表示和客戶端通信的對象。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int uv_accept(uv_stream_t* server, uv_stream_t* client) {
 int err;
 
 switch (client->type) {
  case UV_NAMED_PIPE:
  case UV_TCP:
   // 把fd設置到client中
   err = uv__stream_open(client,
              server->accepted_fd,
              UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   break;
 // ...
 }
 
 client->flags |= UV_HANDLE_BOUND;
 // 標記已經消費了fd
 server->accepted_fd = -1;
 return err;
}

uv_accept主要就是兩個邏輯,把和客戶端通信的fd設置到client中,并標記已經消費,從而驅動剛才講的while循環繼續執行。對于上層來說,就是拿到了一個和客戶端的對象,在Libuv層是結構體,在c++層是一個c++對象,在js層是一個js對象,他們三個是一層層封裝且關聯起來的,最核心的是Libuv的client結構體中的fd,這是和客戶端通信的底層門票。最后回調js層,那就是執行net.js的onconnection。onconnection又封裝了一個Socket對象用于表示和客戶端通信,他持有c++層的對象,c++層對象又持有Libuv的結構體,Libuv結構體又持有fd。

?
1
2
3
4
5
6
7
const socket = new Socket({
  handle: clientHandle,
  allowHalfOpen: self.allowHalfOpen,
  pauseOnCreate: self.pauseOnConnect,
  readable: true,
  writable: true
 });

到此這篇關于nodejs處理tcp連接的核心流程的文章就介紹到這了,更多相關nodejs處理tcp連接內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

延伸 · 閱讀

精彩推薦
  • node.jsNode.js ObjectWrap 的弱引用問題

    Node.js ObjectWrap 的弱引用問題

    最近在寫 Node.js Addon 的過程中,遇到了一個問題,然后發現是 ObjectWrap 弱引用導致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。...

    編程雜技9852022-01-04
  • node.js詳解node.js創建一個web服務器(Server)的詳細步驟

    詳解node.js創建一個web服務器(Server)的詳細步驟

    這篇文章主要介紹了詳解node.js創建一個web服務器(Server)的詳細步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    王佳斌8952021-12-31
  • node.jsk8s node節點重新加入master集群的實現

    k8s node節點重新加入master集群的實現

    這篇文章主要介紹了k8s node節點重新加入master集群的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    Scarborought13922022-01-22
  • node.jsnodejs中使用worker_threads來創建新的線程的方法

    nodejs中使用worker_threads來創建新的線程的方法

    這篇文章主要介紹了nodejs中使用worker_threads來創建新的線程的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友...

    flydean程序那些事8982022-01-06
  • node.jsNode.js 中如何收集和解析命令行參數

    Node.js 中如何收集和解析命令行參數

    這篇文章主要介紹了Node.js 中如何收集和解析命令行參數,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋...

    descire8802021-12-28
  • node.jslinux服務器快速卸載安裝node環境(簡單上手)

    linux服務器快速卸載安裝node環境(簡單上手)

    這篇文章主要介紹了linux服務器快速卸載安裝node環境(簡單上手),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    mose-x8462022-01-22
  • node.js在瀏覽器中,把 Vite 跑起來了!

    在瀏覽器中,把 Vite 跑起來了!

    大家好,我是 ssh,前幾天在推上沖浪的時候,看到 Francois Valdy 宣布他制作了 browser-vite[1],成功把 Vite 成功在瀏覽器中運行起來了。這引起了我的興趣,如...

    前端從進階到入院9282022-01-11
  • node.jsrequire加載器實現原理的深入理解

    require加載器實現原理的深入理解

    這篇文章主要給大家介紹了關于require加載器實現原理的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    隱冬8462022-03-03
主站蜘蛛池模板: 古代翁熄系小说辣文 | 欧美特级特黄a大片免费 | 日韩欧美一区二区三区免费观看 | 日韩性生活片 | 闺蜜的样子小说安沁在线阅读 | 超高清欧美同性videos | 亚洲精品www久久久久久 | 国产在线观看网站 | 国色天香社区视频在线观看免费完整版 | 国产黄频在线观看高清免费 | caoporn超碰| 日韩在线a视频免费播放 | 天天操婷婷 | 色久久一个亚洲综合网 | 狠狠做五月深爱婷婷天天综合 | 国产精品久久久久久网站 | 国产精品久久久久久久久免费hd | adult video在线观看 | 日本一道高清不卡免费 | 加勒比一本大道香蕉在线视频 | 久久国产综合精品欧美 | 国产成人在线小视频 | 国产精品对白刺激久久久 | 天美传媒果冻传媒星空传媒 | 欧美日韩在线观看一区二区 | 视频高h| 三级无删减高清在线影院 | 99热国产在线观看 | 日日操天天射 | 午夜AV国产欧美亚洲高清在线 | 隔壁老王国产精品福利 | 91亚洲精品久久91综合 | 99热精品成人免费观看 | 成人免费网址 | 日本一区二区视频免费播放 | 高h辣h双处全是肉军婚 | 亚洲国产99在线精品一区69堂 | 性柔术18性13处交 | 亚洲成在人网站天堂一区二区 | 极品91| 人人斗地主|