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

nodejs處理tcp連接的核心流程

 更新時間:2021年02月26日 11:14:50   投稿:mrr  
這篇文章主要介紹了nodejs處理tcp連接的核心流程,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

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

int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
 // 設(shè)置處理的請求的策略,見下面的分析
 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;
 // 執(zhí)行bind或設(shè)置標(biāo)記
 err = maybe_new_socket(tcp, AF_INET, flags);
 // 開始監(jiān)聽
 if (listen(tcp->io_watcher.fd, backlog))
  return UV__ERR(errno);
 // 設(shè)置回調(diào)
 tcp->connection_cb = cb;
 tcp->flags |= UV_HANDLE_BOUND;
 // 設(shè)置io觀察者的回調(diào),由epoll監(jiān)聽到連接到來時執(zhí)行
 tcp->io_watcher.cb = uv__server_io;
 // 插入觀察者隊列,這時候還沒有增加到epoll,poll io階段再遍歷觀察者隊列進行處理(epoll_ctl)
 uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);

 return 0;
}

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

void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
 // 循環(huán)處理,uv__stream_fd(stream)為服務(wù)器對應(yīng)的fd
 while (uv__stream_fd(stream) != -1) {
  // 通過accept拿到和客戶端通信的fd,我們看到這個fd和服務(wù)器的fd是不一樣的
  err = uv__accept(uv__stream_fd(stream));
  // uv__stream_fd(stream)對應(yīng)的fd是非阻塞的,返回這個錯說明沒有連接可用accept了,直接返回
  if (err < 0) {
   if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))
    return;
  }
  // 記錄下來
  stream->accepted_fd = err;
  // 執(zhí)行回調(diào)
  stream->connection_cb(stream, 0);
  /*
   stream->accepted_fd為-1說明在回調(diào)connection_cb里已經(jīng)消費了accepted_fd,
   否則先注銷服務(wù)器在epoll中的fd的讀事件,等待消費后再注冊,即不再處理請求了
  */
  if (stream->accepted_fd != -1) {
   uv__io_stop(loop, &stream->io_watcher, POLLIN);
   return;
  }
 /*
   ok,accepted_fd已經(jīng)被消費了,我們是否還要繼續(xù)accept新的fd,
   如果設(shè)置了UV_HANDLE_TCP_SINGLE_ACCEPT,表示每次只處理一個連接,然后
   睡眠一會,給機會給其他進程accept(多進程架構(gòu)時)。如果不是多進程架構(gòu),又設(shè)置這個,
   就會導(dǎo)致處理連接被延遲了一下
 */
  if (stream->type == UV_TCP &&
    (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {
   struct timespec timeout = { 0, 1 };
   nanosleep(&timeout, NULL);
  }
 }
}

從uv__server_io,我們知道Libuv在一個循環(huán)中不斷accept新的fd,然后執(zhí)行回調(diào),正常來說,回調(diào)會消費fd,如此循環(huán),直到?jīng)]有連接可處理了。接下來,我們重點看看回調(diào)里是如何消費fd的,大量的循環(huán)會不會消耗過多時間導(dǎo)致Libuv的事件循環(huán)被阻塞一會。tcp的回調(diào)是c++層的OnConnection。

// 有連接時觸發(fā)的回調(diào)
template <typename WrapType, typename UVType>
void ConnectionWrap<WrapType, UVType>::OnConnection(uv_stream_t* handle,
                          int status) {
 // 拿到Libuv結(jié)構(gòu)體對應(yīng)的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所對應(yīng)的c++層對象存到wrap中
  ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj);
  // 拿到對應(yīng)的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());
 }
 // 回調(diào)js,client_handle相當(dāng)于在js層執(zhí)行new TCP
 Local<Value> argv[] = { Integer::New(env->isolate(), status), client_handle };
 wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv);
}

代碼看起來很復(fù)雜,我們只需要關(guān)注uv_accept。uv_accept的參數(shù),第一個是服務(wù)器對應(yīng)的handle,第二個是表示和客戶端通信的對象。

int uv_accept(uv_stream_t* server, uv_stream_t* client) {
 int err;

 switch (client->type) {
  case UV_NAMED_PIPE:
  case UV_TCP:
   // 把fd設(shè)置到client中
   err = uv__stream_open(client,
              server->accepted_fd,
              UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   break;
 // ...
 }

 client->flags |= UV_HANDLE_BOUND;
 // 標(biāo)記已經(jīng)消費了fd
 server->accepted_fd = -1;
 return err;
}

uv_accept主要就是兩個邏輯,把和客戶端通信的fd設(shè)置到client中,并標(biāo)記已經(jīng)消費,從而驅(qū)動剛才講的while循環(huán)繼續(xù)執(zhí)行。對于上層來說,就是拿到了一個和客戶端的對象,在Libuv層是結(jié)構(gòu)體,在c++層是一個c++對象,在js層是一個js對象,他們?nèi)齻€是一層層封裝且關(guān)聯(lián)起來的,最核心的是Libuv的client結(jié)構(gòu)體中的fd,這是和客戶端通信的底層門票。最后回調(diào)js層,那就是執(zhí)行net.js的onconnection。onconnection又封裝了一個Socket對象用于表示和客戶端通信,他持有c++層的對象,c++層對象又持有Libuv的結(jié)構(gòu)體,Libuv結(jié)構(gòu)體又持有fd。

const socket = new Socket({
  handle: clientHandle,
  allowHalfOpen: self.allowHalfOpen,
  pauseOnCreate: self.pauseOnConnect,
  readable: true,
  writable: true
 });

到此這篇關(guān)于nodejs處理tcp連接的核心流程的文章就介紹到這了,更多相關(guān)nodejs處理tcp連接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • VSCode launch.json配置詳細(xì)教程

    VSCode launch.json配置詳細(xì)教程

    這篇文章主要介紹了vscode 的node.js debugger 的 launch.json 配置詳情,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • 快速刪除 node_modules 目錄的集中方法(多種方法)

    快速刪除 node_modules 目錄的集中方法(多種方法)

    本文介紹了三種快速刪除node_modules目錄的方法:使用rimraf工具、通過npx運行rimraf以及在Windows命令提示符中使用del命令,每種方法都適合不同的操作系統(tǒng)和使用場景
    2024-11-11
  • 小結(jié)Node.js中非阻塞IO和事件循環(huán)

    小結(jié)Node.js中非阻塞IO和事件循環(huán)

    本文針對在Node.js關(guān)鍵的兩個概念:非阻塞IO和事件循環(huán)進行了適當(dāng)?shù)目偨Y(jié),需要的朋友可以參考下
    2014-09-09
  • Node.js下自定義錯誤類型詳解

    Node.js下自定義錯誤類型詳解

    在JavaScript里面,運行過程中的錯誤的類型總是被人忽略,這篇文章給大家詳細(xì)介紹了如何在Node.js下自定義錯誤類型,對大家學(xué)習(xí)或者使用Node.js具有一定的參考借鑒價值,有需要的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • nodejs超出最大的調(diào)用棧錯誤問題

    nodejs超出最大的調(diào)用棧錯誤問題

    這篇文章主要介紹了nodejs超出最大的調(diào)用棧錯誤問題,需要的朋友可以參考下
    2017-12-12
  • Node.js?模塊的加載邏輯你了解嘛

    Node.js?模塊的加載邏輯你了解嘛

    這篇文章主要為大家介紹了Node.js?模塊的加載邏輯,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • node.js學(xué)習(xí)之base64編碼解碼

    node.js學(xué)習(xí)之base64編碼解碼

    開發(fā)者對Base64編碼肯定很熟悉,是否對它有很清晰的認(rèn)識就不一定了。實際上Base64已經(jīng)簡單到不能再簡單了,這篇文章給大家通過示例代碼介紹了node.js對字符串和圖片base64編碼解碼的方法,有需要的朋友們可以通過本文來進行學(xué)習(xí),下面來一起看看吧。
    2016-10-10
  • Nginx設(shè)置為Node.js的前端服務(wù)器方法總結(jié)

    Nginx設(shè)置為Node.js的前端服務(wù)器方法總結(jié)

    在本篇文章中小編給大家分享了關(guān)于Nginx設(shè)置為Node.js的前端服務(wù)器的方法和實例,需要的朋友們學(xué)習(xí)下。
    2019-03-03
  • 簡單談?wù)刵ode.js 版本控制 nvm和 n

    簡單談?wù)刵ode.js 版本控制 nvm和 n

    大量開發(fā)者的貢獻使Node版本的迭代速度很快,版本很多(橫跨0.6到0.11),所以升級Node版本就成為了一個問題。目前有n和nvm這兩個工具可以對Node進行無痛升級,本文簡單介紹一下二者的使用。
    2015-10-10
  • nodejs基于mssql模塊連接sqlserver數(shù)據(jù)庫的簡單封裝操作示例

    nodejs基于mssql模塊連接sqlserver數(shù)據(jù)庫的簡單封裝操作示例

    這篇文章主要介紹了nodejs基于mssql模塊連接sqlserver數(shù)據(jù)庫的簡單封裝操作,結(jié)合實例形式分析了nodejs中mssql模塊的安裝與操作sqlserver數(shù)據(jù)庫相關(guān)使用技巧,需要的朋友可以參考下
    2018-01-01

最新評論

普兰店市| 蓝田县| 曲水县| 民丰县| 连云港市| 青川县| 西峡县| 铜山县| 武穴市| 阳原县| 新乡市| 焦作市| 大悟县| 会昌县| 十堰市| 安阳市| 肥西县| 鹤岗市| 牙克石市| 神池县| 乌鲁木齐县| 乌审旗| 石家庄市| 盘山县| 沙坪坝区| 雷波县| 利津县| 望都县| 蓝山县| 林甸县| 志丹县| 石城县| 湟中县| 富阳市| 巴青县| 大洼县| 襄城县| 洛南县| 门头沟区| 安溪县| 仪陇县|