tcp接続を処理するnodejsのコアプロセスを簡単に要約します

この記事では、主にnodejs処理のtcp接続のコアプロセスを紹介します。この記事では、すべての人の研究や仕事のための特定の参照値を持つサンプルコードを通じて詳細に紹介します。それを必要とする友人は参照できます。

数日前、nodejsでのepollとリクエストの処理に関する知識を小さなパートナーと交換しました。今日は、nodejsでリクエストを処理するロジックについて簡単に説明します。リッスン機能から始めます。

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); / *デフォルトでオフ。* / 
 if(single_accept)
  tcp-> flags | = UV_HANDLE_TCP_SINGLE_ACCEPT; 
 //バインドを実行するかフラグを設定する
 err = may_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; 
 // Set ioウォッチャーのコールバックは、
 epollが接続の到着を監視するときに実行されます。tcp-> io_watcher.cb = uv__server_io;
 //オブザーバーキューを挿入します。現時点では、epollに追加されていませんステージで、オブザーバーキューをトラバースして処理します(epoll_ctl)
 uv__io_start(tcp-> loop、&tcp-> io_watcher、POLLIN); 
 
 0を返します。
}

createServerを使用すると、Libuvレイヤーが従来のネットワークプログラミングのロジックであることがわかります。この時点で私たちのサービスが開始されます。投票段階では、リスニングファイル記述子とコンテキスト(対象のイベント、コールバックなど)がepollに登録されます。通常、epollでブロックされます。では、この時点でtcp接続が確立されるとどうなりますか?Epollは、最初にイベントをトリガーしたfdをトラバースし、次にfdコンテキスト、つまりuvserver_ioでコールバックを実行します。uvserver_ioを見てみましょう。

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){ 
  //クライアントを取得します終了通信のfdを受け入れると、このfdがサーバーのfdとは異なることがわかります
  err = uv__accept(uv__stream_fd(stream)); 
  // uv__stream_fd(stream)に対応するfdは非ブロッキングです。このエラーを返すと、受け入れることができる接続がないこと。、直接返す
  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であり、accepted_fdがコールバックconnection_cbで消費されたことを示します。
   そうでない場合、サーバーのepollのfdの読み取りイベントが最初にキャンセルされます消費後に登録されます。つまり、リクエストは処理されなくなります
  * /
  if(
   stream- > accepted_fd!= -1){ uv__io_stop(loop、&stream-> io_watcher、POLLIN); 
   return; 
  } 
 / * 
   ok、accepted_fdが消費されましたが、
   設定されている場合、新しいfdを引き続き受け入れますかUV_HANDLE_TCP_SINGLE_ACCEPTは、一度に1つの接続が処理され、
   しばらくスリープして、他のプロセスが受け入れる機会を与えます(マルチプロセスアーキテクチャの場合)。マルチプロセスアーキテクチャでない場合、これを再度設定する
   と、接続の処理に遅延が発生します
 * / 
  if(stream-> type == UV_TCP && stream- > flags&UV_HANDLE_TCP_SINGLE_ACCEPT)){ 
   struct timespec timeout = {0、 1}; 
   nanosleep(&timeout、NULL); 
  } 
 } 
}

uv__server_ioから、Libuvは常にループ内の新しいfdを受け入れ、コールバックを実行することがわかります。通常、コールバックはfdを消費し、以下同様にプロセスへの接続がなくなるまで続きます。次に、コールバックでfdがどのように消費されるかに焦点を当てましょう。ループの数が多いと時間がかかりすぎて、Libuvのイベントループがしばらくブロックされます。tcpコールバックは、c ++レイヤーのOnConnectionです。

//接続
テンプレートがある場合にコールバックがトリガーされ
ますvoidConnectionWrap :: OnConnection(uv_stream_t * handle、
                          int status){ 
 // Libuv構造に対応するc ++レイヤーオブジェクト
 WrapType *を取得します                          wrap_data = static_cast(handle-> data); 
 CHECK_EQ( &wrap_data-> handle_、reinterpret_cast(handle)); 
 
 Environment * env = wrap_data-> env(); 
 HandleScope handle_scope(env-> isolate()); 
 Context :: Scope context_scope(env-> context()); 
 
 //および顧客は
 、ローカルclient_handleを通信するオブジェクト側; 
 
 IF(Status == 0){ 
  //オブジェクトをインスタンス化してクライアントJavaScriptとハンドルを処理し
  ますローカルclient_obj
  を使用しjsオブジェクトのレイヤーを作成します; 
   return; 
  IF(WrapType :: Instantiate(env 、wrap_data、WrapType!:: SOCKET)
       .ToLocal(&client_obj)) 
 wrap_data-> MakeCallback(env-> onconnection_string()、arraysize(argv)、argv);
 
  //クライアントのjavascriptオブジェクトをアンラップします
  。WrapType* wrap; 
  // jsレイヤーで使用されるオブジェクトclient_objに対応するc ++レイヤーオブジェクトをラップに
  格納しますASSIGN_OR_RETURN_UNWRAP(&wrap、client_obj); 
  //対応するハンドルを取得し
  ますuv_stream_t * client = reinterpret_cast (&wrap-> handle_); 
  // handleaccpetからfdの1つを取得してクライアントに保存すると、クライアントはクライアントと通信できます
  if(uv_accept(handle、client))
   return; 
  client_handle = client_obj; 
 } else { 
  client_handle = Undefined(env-> 
 isolate ()); 
 } //コールバックjs、client_handleは新しいTCP
 ローカルの実行と同等ですargv [] = {Integer :: New(env-> isolate()、status)、client_handle}; 
}

コードは複雑に見えます。uv_acceptに焦点を合わせるだけです。uv_acceptのパラメーター。最初のパラメーターはサーバーに対応するハンドルで、2番目のパラメーターはクライアントと通信するオブジェクトです。

int uv_accept(uv_stream_t * server、uv_stream_t * client){ 
 int err; 
 
 スイッチ(クライアント- >型){
  ケースUV_NAMED_PIPE:
  ケースUV_TCP:
   //把FD设置到クライアント中
   ERR = uv__stream_open(クライアント、
              サーバ- > accepted_fd、
              UV_HANDLE_READABLE | UV_HANDLE_WRITABLE)。
   ブレーク; 
 // ... 
 } 
 
 client-> flags | = UV_HANDLE_BOUND; 
 //¸记已经消费了fd
 サーバー-> accepted_fd = -1; 
 エラーを返します。
}

uv_acceptは主に2つのロジックであり、クライアントと通信するfdをクライアントに設定し、それが消費されたことをマークします。これにより、前述のwhileループを駆動して実行を続行します。上位層の場合、クライアントでオブジェクトを取得します。これは、Libuv層の構造、c ++層のc ++オブジェクト、およびjs層のjsオブジェクトです。これらの3つは、カプセル化され、接続された層です。コアはLibuvのクライアント構造のfdであり、これはクライアントと通信するための基礎となるチケットです。jsレイヤーへの最後のコールバックは、net.jsのonconnectionを実行することです。onconnectionは、クライアントと通信するためのSocketオブジェクトをカプセル化します。これは、c ++レイヤーオブジェクトを保持し、c ++レイヤーオブジェクトはLibuv構造を保持し、Libuv構造はfdを保持します。

const socket = new Socket({
  ハンドル:clientHandle、
  allowHalfOpen:self.allowHalfOpen、
  pauseOnCreate:self.pauseOnConnect、
  読み取り可能:true、
  書き込み可能:true 
 }); 
const socket = new Socket({
  ハンドル:clientHandle、
  allowHalfOpen:self.allowHalfOpen、
  pauseOnCreate:self.pauseOnConnect、
  読み取り可能:true、
  書き込み可能:true 
 });

これまでのところ、tcp接続を処理するnodejsのコアプロセスに関するこの記事を紹介しました。ご支援いただきありがとうございます。

おすすめ

転載: blog.csdn.net/yaxuan88521/article/details/114686264