Ir al contenido principal

En este post deseo discutir un error de codificación común que puede resultar en que un módulo se quede sin o por lo menos agote seriamente el número disponible de clones de dispositivos stcp para su uso en la creación de sockets TCP.

Cada llamada a la función socket da como resultado que STCP cree un nuevo clon del dispositivo stcp. El número máximo de clones que se pueden crear se controla mediante el campo clone_limit en la entrada devices.tin.

/    =nombre               stcp.m17
     =nombre_módulo        m17
     =tipo_dispositivo        flujos
     =nombre_lista_acceso   stcp_access
     =controlador_flujos     stcp
     =límite_clonación        5120
     =comentario            Proporciona API TCP

Puede ver cuántos clones hay actualmente en uso volcando la estructura del dispositivo en analyze_system y mirando el valor clone_count. Si clone_count es igual a clone_limit, las llamadas a la función socket devolverán un error e$clone_limit_exceeded: «Se ha superado el límite de clones para el dispositivo».

como:  clon coincidente; dump_dvt -nombre stcp.m17
límite de clonación:       5120
clone_count:       42
cloned_from:       -27271
remote_clone_limit: 0

En general, la llamada al socket que crea el dispositivo clonado va seguida de un conectar o una llamada bind y listen . En ese momento, se puede ver la entrada correspondiente al ejecutar el comando «netstat».

netstat -numeric -all_sockets
Conexiones activas (incluidos servidores)
Proto Recv-Q Send-Q  Dirección local      Dirección externa    (estado)
tcp        0      0  172.16.124.217:23  192.168.109.22:50038 ESTABLISHED
tcp        0      0  172.16.124.217:22  172.16.124.24:54987 ESTABLECIDA
tcp        0      0  10.20.1.1:37       10.20.1.26:1528    TIME_WAIT
tcp        0      0  10.20.1.1:37       10.20.1.27:1579    TIME_WAIT
tcp        0      0  172.16.124.217:61780 192.168.109.22:23 ESTABLECIDA
tcp        0      0  172.16.124.217:22  172.16.124.50:17421 ESTABLISHED
tcp        0      0  172.16.124.217:22  172.16.124.50:17658 ESTABLECIDA
tcp        0      0  *:23               *:*                ESCUCHAR
tcp        0      0  *:6666             *:*                LISTEN
tcp        0      0  *:21               *:*                LISTEN
tcp        0      0  *:3000             *:*                LISTEN
tcp        0      0  *:7                *:*                LISTEN
tcp        0      0  *:9                *:*                LISTEN
tcp        0      0  *:13               *:*                LISTEN
tcp        0      0  *:19               *:*                LISTEN
tcp        0      0  *:37               *:*                LISTEN
tcp        0      0  *:901              *:*                LISTEN
tcp        0      0  *:1414             *:*                LISTEN
tcp        0      0  *:81               *:*                LISTEN
tcp        0      0  10.20.1.1:37       10.20.1.9:3633     TIME_WAIT
tcp        0     50  10.10.1.1:52653    10.10.1.200:3001   ESTABLISHED
tcp        0      0  10.10.1.1:52624    10.10.1.200:3001   FIN_WAIT_1
tcp        0      0  10.20.1.1:61704    10.20.1.3:48879    ESTABLECIDA
tcp        0      0  *:3001             *:*                ESCUCHAR
tcp        0      0  *:3002             *:*                ESCUCHAR
tcp        0      0  *:3003             *:*                LISTEN
tcp        0      0  *:4000             *:*                LISTEN
tcp        0      0  172.16.124.217:4000 172.16.124.78:1024 ESTABLISHED
tcp        0      0  172.16.124.217:4000 172.16.124.227:1025 ESTABLECIDO
tcp        0      0  *:4001             *:*                ESCUCHAR
tcp        0      0  *:4002             *:*                ESCUCHAR
tcp        0      0  *:4003             *:*                LISTEN
tcp        0      0  *:4004             *:*                LISTEN
tcp        0      0  *:22               *:*                LISTEN
tcp        0      0  *:4005             *:*                LISTEN
tcp        0      0  *:4006             *:*                LISTEN
tcp        0      0  172.16.124.217:4006 172.16.124.203:49231 ESTABLISHED
tcp        0      0  *:4007             *:*                LISTEN
tcp        0      0  *:4008             *:*                LISTEN
tcp        0      0  *:4009             *:*                LISTEN
tcp        0      0  172.16.124.217:4008 172.16.124.203:49262 ESTABLISHED
tcp        0      0  *:4010             *:*                LISTEN
tcp        0      0  *:4011             *:*                LISTEN
tcp        0      0  *:4012             *:*                LISTEN
tcp        0      0  *:4013             *:*                LISTEN
tcp        0      0  *:4014             *:*                LISTEN
tcp        0      0  *:4015             *:*                LISTEN
tcp        0      0  *:80               *:*                LISTEN
tcp        0      0  *:9182             *:*                LISTEN
tcp        0      0  *:445              *:*                LISTEN
tcp        0      0  *:139              *:*                LISTEN
tcp        0      0  10.20.1.1:53495    10.20.1.9:48879    ESTABLISHED
tcp        0      0  10.20.1.1:61703    10.20.1.3:48879    ESTABLECIDA
tcp        0      0  10.20.1.1:61707    10.20.1.3:48879    ESTABLECIDA
tcp        0      0  10.20.1.1:61705    10.20.1.9:48879    ESTABLECIDA
tcp        0      0  10.20.1.1:61709    10.20.1.9:48879    ESTABLECIDA
tcp        0      0  10.20.1.1:61710    10.20.1.9:48879    ESTABLECIDA
tcp        0      0  172.16.124.217:61789 172.16.124.203:4000 ESTABLECIDA
tcp        0    400  172.16.124.217:22  172.16.124.50:17674 ESTABLECIDA

Si cuenta el número de líneas, verá que hay más de 42, esto se debe a que no todas las entradas que muestra netstat utilizan un clon de dispositivo stcp. Por ejemplo, las conexiones OSL y las conexiones X25_cpc utilizadas con NIO. Eche un vistazo a socket_count.cm para obtener más detalles.

Si se realiza una llamada a socket sin conectarse o enlazar, o si el enlace falla, se puede crear el problema contrario: el valor de clone_count es mayor que el número de entradas que muestra netstat.

como:  clon coincidente; dump_dvt -nombre stcp.m17
límite de clonación:       5120
clone_count:       4131
cloned_from:       -23179
remote_clone_limit: 0
as:

No voy a incluir de nuevo el resultado de netstat, pero créeme, no ha cambiado con respecto al ejemplo anterior.

Esta situación, un clon adicional (4131 – 42) y aparentemente no contabilizado, del dispositivo STCP fue creada por el siguiente fragmento de código. El código llama a la función socket seguida de la función bind. Si el bind falla, se repite el bucle. Muchas aplicaciones añadirían un temporizador para esperar 1, 60 o 300 segundos y volver a intentarlo, lo que solo retrasa lo inevitable, suponiendo, por supuesto, que la condición que causa el error no desaparezca.

tryAgain = 1;
while (tryAgain)
  {
  if ((socks0 = socket (AF_INET, SOCK_STREAM, 0)) < 0)
     {
     if (debugFlag)
        perror ("badService: can't create listening socket");
     }
  else {
/* build a sockaddr structure holding the address we will bind to.
   The IP address is INADDR_ANY meaning we will listen on all active
   IP addresses */

     bzero ( (char *) &serv_addr, sizeof (serv_addr));
     serv_addr.sin_family        = AF_INET;
     serv_addr.sin_addr.s_addr   = htonl (INADDR_ANY);
     serv_addr.sin_port          = htons (portNumber);

/* now bind to the address and port */
     if (bind (socks0, (struct sockaddr *) &serv_addr,
                                      sizeof (serv_addr)) < 0)
        {
        if (debugFlag)
           perror ("badService: can't bind address, trying again");
        }
     else
        tryAgain = 0;
     }
   }

El error más común es que otro proceso ya se ha vinculado al puerto solicitado. Independientemente del motivo del error, la solución es cerrar el socket después de informar del error de vinculación.

tryAgain = 1;
while (tryAgain)
  {
  if ((socks0 = socket (AF_INET, SOCK_STREAM, 0)) < 0)
     {
     if (debugFlag)
        perror ("goodService: can't create listening socket");
     }
  else {
/* build a sockaddr structure holding the address we will bind to.
   The IP address is INADDR_ANY meaning we will listen on all active
   IP addresses */

     bzero ( (char *) &serv_addr, sizeof (serv_addr));
     serv_addr.sin_family        = AF_INET;
     serv_addr.sin_addr.s_addr   = htonl (INADDR_ANY);
     serv_addr.sin_port          = htons (portNumber);

/* now bind to the address and port */
     if (bind (socks0, (struct sockaddr *) &serv_addr,
                                      sizeof (serv_addr)) < 0)
        {
        if (debugFlag)
           perror ("goodService: can't bind address, trying again");
        if (close (socks0) < 0)
           if (debugFlag)
              perror ("goodService: can't close old socket");
        }
     else
        tryAgain = 0;
     }
   }

 

Esta publicación ha tratado sobre cómo alcanzar el límite clone_limit debido a un error de codificación, pero ¿qué pasa si no hay ningún error, si el entorno de la aplicación realmente está utilizando todos esos dispositivos clonados? En ese caso, suponiendo que no haya alcanzado el límite del sistema de 16 000, puede aumentar el límite. Debe actualizar el campo clone_limit del dispositivo stcp en el archivo devices.tin y volver a crear el archivo devices.table. Si utiliza una versión 17.1 o posterior, puede utilizar el comando update_device_info para aumentar el límite del arranque actual y confiar en el archivo devices.table actualizado para el siguiente arranque. En versiones anteriores a la 17.1, la única opción real es reiniciar el sistema. Debe establecer el límite en un valor que se corresponda con sus necesidades actuales más el crecimiento previsto; no debe limitarse a aumentar el límite a 16 000. Aunque ahora no tenga ningún error de aplicación que consuma dispositivos clonados, no hay garantía de que no vaya a tenerlo en el futuro. Una aplicación que consuma todos los dispositivos clonados disponibles también consumirá una gran cantidad de memoria de flujos, y agotar la memoria de flujos afectará negativamente a las conexiones TCP existentes.