Resuma brevemente el proceso central del procesamiento de la conexión tcp de nodejs

Este artículo presenta principalmente el proceso principal del procesamiento de la conexión tcp de nodejs. Este artículo lo presenta en detalle a través de un código de ejemplo, que tiene un cierto valor de referencia para el estudio o el trabajo de todos. Los amigos que lo necesiten pueden consultar

Hace unos días, intercambié algunos conocimientos sobre epoll y procesamiento de solicitudes en nodejs con un pequeño socio. Hoy, hablemos brevemente sobre la lógica del procesamiento de solicitudes de nodejs. Empezamos con la función de escuchar.

int uv_tcp_listen (uv_tcp_t * tcp, int backlog, uv_connection_cb cb) { 
 // Establecer la estrategia para procesar la solicitud, ver el análisis a continuación 
 if (single_accept == -1) { 
  const char * val = getenv ("UV_TCP_SINGLE_ACCEPT"); 
  single_accept = (val! = NULL && atoi (val)! = 0); / * Desactivado por defecto. * / 
 ) 
 if (single_accept) 
  tcp-> flags | = UV_HANDLE_TCP_SINGLE_ACCEPT; 
 // Ejecutar bind o establecer flag 
 err = maybe_new_socket (tcp, AF_INET, flags); 
 // Empieza a escuchar 
 if (listen (tcp-> io_watcher.fd, backlog)) 
  return UV__ERR (errno); 
 // Establecer callback 
 tcp-> connection_cb = cb; 
 tcp-> flags | = UV_HANDLE_BOUND; 
 // Establecer La devolución de llamada del io watcher se ejecuta cuando epoll monitorea la llegada de la conexión 
 tcp-> io_watcher.cb = uv__server_io;
 // Insertar la cola de observadores. En este momento, no se ha agregado a epoll. En la etapa de poll io, atraviesa la cola de observadores para procesar (epoll_ctl)
 uv__io_start (tcp-> bucle, & tcp-> io_watcher, POLLIN); 
 
 return 0; 
}

Vemos que cuando creamosServer, la capa Libuv es la lógica de la programación de red tradicional. En este momento se inicia nuestro servicio. En la etapa de poll io, nuestros descriptores de archivo de escucha y contexto (eventos de interés, devoluciones de llamada, etc.) se registrarán en epoll. Normalmente está bloqueado en epoll. Entonces, ¿qué sucede cuando llega una conexión tcp en este momento? Epoll primero atraviesa el fd que desencadenó el evento y luego ejecuta la devolución de llamada en el contexto fd, es decir, uvserver_io. Echemos un vistazo a uvserver_io.

void uv__server_io (uv_loop_t * loop, uv__io_t * w, unsigned int events) { 
 // Procesamiento de bucle, uv__stream_fd (stream) es el fd correspondiente al servidor 
 while (uv__stream_fd (stream)! = -1) { 
  // Obtener el cliente a través accept El fd de la comunicación final, vemos que este fd es diferente del fd del servidor 
  err = uv__accept (uv__stream_fd (stream)); 
  // El fd correspondiente a uv__stream_fd (stream) no es bloqueante. Devolver este error significa que no hay conexión disponible para aceptar., Regrese directamente 
  if (err <0) { 
   if (err == UV_EAGAIN || err == UV__ERR (EWOULDBLOCK)) 
    return; 
  } 
  // 
  regístrelo stream-> accept_fd = err; 
  / / execute callback 
  stream-> connection_cb (stream, 0); 
  / * 
   stream-> accept_fd es -1, lo que indica que accept_fd se ha consumido en la devolución de llamada connection_cb; de lo 
   contrario , el evento de lectura del fd en epoll del servidor se cancelará primero , y se registrará después del consumo, es decir, la solicitud ya no se tramitará 
  * /
  if (stream-> accept_fd! = -1) { 
   uv__io_stop (loop, & stream-> io_watcher, POLLIN); 
   return; 
  } 
 / * 
   ok, accept_fd se ha consumido, seguimos aceptando un nuevo fd, 
   si se establece UV_HANDLE_TCP_SINGLE_ACCEPT significa que solo una conexión se procesa a la vez, y luego 
   duerme por un tiempo, dando a otros procesos la oportunidad de aceptar (en arquitectura multiproceso). Si no se trata de una arquitectura multiproceso, volver a configurarlo 
   provocará un retraso en el procesamiento de la conexión 
 * / 
  if (stream-> type == UV_TCP && 
    (stream-> flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { 
   struct timespec timeout = {0, 1}; 
   nanosleep (& timeout, NULL); 
  } 
 } 
}

Desde uv__server_io, sabemos que Libuv acepta constantemente nuevos fd en un bucle y luego ejecuta la devolución de llamada. Normalmente, la devolución de llamada consume fd, y así sucesivamente hasta que no hay conexión para procesar. A continuación, centrémonos en cómo se consume fd en las devoluciones de llamada. ¿Una gran cantidad de bucles consumirá demasiado tiempo y hará que el bucle de eventos de Libuv se bloquee por un tiempo? La devolución de llamada tcp es OnConnection de la capa c ++.

// Devolución de llamada activada cuando hay una 
plantilla de conexión 
void ConnectionWrap :: OnConnection (uv_stream_t * handle, 
                          int status) { 
 // Obtiene el objeto de capa c ++ 
 WrapType * correspondiente a la estructura de Libuv                           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 ()); 
 
 // y cliente un lado del objeto para comunicar 
 el client_handle local; 
 
 IF (Status == 0) { 
  // Crear una 
  instancia del objeto el JavaScript del cliente y manejar. // Crear una capa de objetos js usando 
  el client_obj local; 
  IF (WrapType :: Instantiate (el env , wrap_data, WrapType !:: SOCKET) 
       .ToLocal (& client_obj)) 
   return;
 
  // Desenvuelve el objeto javascript del cliente. 
  WrapType * wrap; 
  // Almacena el objeto de la capa c ++ correspondiente al objeto client_obj usado por la capa js en la envoltura 
  ASSIGN_OR_RETURN_UNWRAP (& wrap, client_obj); 
  // Obtén el identificador correspondiente 
  uv_stream_t * client = reinterpret_cast (& wrap-> handle_); 
  // Tome uno de los fd de handleaccpet y guárdelo en el cliente, y el cliente puede comunicarse con el cliente 
  if (uv_accept (handle, client)) 
   return; 
  client_handle = client_obj; 
 } else { 
  client_handle = Undefined (env-> isolate ()); 
 } 
 // callback js, client_handle es equivalente a ejecutar un nuevo TCP 
 Local argv [] = {Integer :: New (env-> isolate (), status), client_handle};  
 wrap_data-> MakeCallback (env-> onconnection_string (), arraysize (argv), argv);
}

El código parece complicado, solo necesitamos enfocarnos en uv_accept. Los parámetros de uv_accept, el primero es el handle correspondiente al servidor, y el segundo es el objeto que se comunica con el cliente.

int uv_accept (uv_stream_t * servidor, uv_stream_t * cliente) { 
 int err; 
 
 switch (cliente-> tipo) { 
  caso UV_NAMED_PIPE: 
  caso UV_TCP: 
   // 把 fd 设置 到 cliente 中
   err = uv__stream_open (cliente, 
              servidor-> aceptado_fd, 
              UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); 
   descanso; 
 // ... 
 } 
 
 cliente-> banderas | = UV_HANDLE_BOUND; 
 // 标记 已经 消费 了
 servidor fd- > aceptado_fd = -1; 
 return err; 
}

uv_accept es principalmente dos lógicas, establece el fd que se comunica con el cliente al cliente y marca que se ha consumido, impulsando así el ciclo while que acabamos de mencionar para continuar con la ejecución. Para la capa superior, es obtener un objeto con el cliente. Es una estructura en la capa Libuv, un objeto c ++ en la capa c ++ y un objeto js en la capa js. Los tres están encapsulados y conectados a la capa por capa. El núcleo es el fd en la estructura del cliente de Libuv, que es el ticket subyacente para comunicarse con el cliente. La última devolución de llamada a la capa js es ejecutar la conexión de net.js. onconnection encapsula un objeto Socket para comunicarse con el cliente. Contiene el objeto de la capa c ++, el objeto de la capa c ++ contiene la estructura Libuv y la estructura Libuv contiene el fd.

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

Hasta ahora, se presenta este artículo sobre el proceso central del procesamiento de la conexión tcp de nodejs, gracias por su apoyo.

Supongo que te gusta

Origin blog.csdn.net/yaxuan88521/article/details/114686264
Recomendado
Clasificación