2010-12-03 15 views
6

Aquí está el fondo rápido: tengo un cliente y un programa de servidor que se comunican entre sí a través de un socket Unix. Cuando analizo los mensajes recibidos en el lado del servidor, intento usar strncmp para determinar qué acción tomar.strncmp uso correcto

El problema que tengo es averiguar exactamente qué usar para el argumento de longitud de strncmp. La razón por la cual esto es problemático es porque algunos de mis mensajes comparten un prefijo común. Por ejemplo, tengo un mensaje "getPrimary", que hace que el servidor responda con una dirección de servidor primaria, y un mensaje "getPrimaryStatus", que hace que el servidor responda con el estado del servidor primario. Mi idea inicial era hacer lo siguiente:

if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){ 
    return foo; 
} 
else if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){ 
    return bar; 
} 

El problema con esto es cuando envío el servidor "getPrimaryStatus", el código siempre devolverá foo porque strncmp no está comprobando lo suficientemente lejos en la cadena. Podría pasar strlen (mensaje) como argumento de longitud a strncmp, pero esto parece frustrar el propósito de usar strncmp, que es para evitar el desbordamiento en el caso de una entrada inesperada. Tengo una variable estática para la longitud máxima de mensaje que puedo leer, pero parece que se pasa esto porque la longitud solo garantiza que si el mensaje se desborda, los efectos se reducen al mínimo.

He encontrado algunas soluciones, pero no son muy bonitas, así que me preguntaba si habría una forma común de resolver este problema. Como referencia, mis soluciones actuales son: . Ordene mis declaraciones if/else if de tal manera que los mensajes con prefijos comunes se comprueben en orden de longitud descendente (lo que parece una buena manera de lanzar una mina antipersonal). en mi código para cualquiera que intente agregarle algo más adelante).

Grupo mis mensajes con prefijos comunes juntos y buscar la primera sufijo:

if(strncmp(message,"getPrimary",strlen("getPrimary"))==0){ 
    if(strncmp(message,"getPrimaryStatus",strlen("getPrimaryStatus"))==0){ 
     return bar; 
    else 
     return foo; 
    } 
} 

pero es algo que se siente desordenado, sobre todo porque tengo alrededor de 20 diferentes mensajes posibles que estoy manejando.

Crear una matriz de todos los mensajes posibles que tengo, añadir una función a mi secuencia init que ordenará la matriz descendiendo la longitud, y hacer que mi código busque los elementos de esa lista hasta que encuentre una coincidencia. Esto parece complicado y tonto.

Parece que esto debería ser un problema bastante común que debería haber una solución para ello en alguna parte, pero no he podido encontrar nada hasta el momento.

¡Gracias de antemano por la ayuda!

Respuesta

4

Suponiendo que la cadena de message es supone ser terminada en nulo, la única razón para usar strncmp() aquí en vez de strcmp() sería la de evitar que mirar más allá del final de message, en el caso en message es no nulo-terminado.

Como tal, el n se pasa a strncmp() debería ser el tamaño recibido de message, lo que usted debe saber (desde el valor de retorno de la función read()/recv() que leyó el mensaje).

+0

Parece que esta es la solución más simple. Esta es realmente la primera vez que uso conectores de Unix para facilitar la comunicación entre procesos, y escribí el código de manejo del socket hace aproximadamente dos meses ... ¡Había olvidado por completo que estaba usando read(), que devuelve el número de bytes leídos! –

+0

Creo que pasar el tamaño del mensaje a strncmp es una solución incompleta a su problema. Por favor, mira mi solución para más detalles. –

+0

Las comparaciones entre cadenas con muchos caracteres coincidentes se pueden realizar de forma más eficiente utilizando un enfoque de "fragmentación", mientras que las comparaciones entre cadenas que difieren desde el principio pueden ser más eficientes utilizando un enfoque de carácter a la vez. No me sorprendería que algunas implementaciones de strncmp usen fragmentación cuando "n" es grande, aunque sería mejor si hubiera funciones separadas para los casos de uso donde es probable la coincidencia con aquellos donde es poco probable. – supercat

0

Sacando de mi memoria la programación en C hace un año, creo que se supone que el tercer argumento le dice a la función cuántos caracteres procesar para la comparación. Es por eso que es seguro, ya que tiene el control sobre el número de caracteres a procesar

Así debería ser algo como:

if(strncmp(message, "getPrimary", strlen("getPrimary")) { 
    // 
} 
0

No utilice strncmp(). Use strlcmp() en su lugar. Es más seguro.

+2

strlcmp() no parece ser parte de ningún estándar; y si las implementaciones que encontré en línea son típicas, entonces su comportamiento degenera en el comportamiento de strncmp() si una cadena es un subconjunto de la otra. Eso es exactamente lo que el OP no quiere. –

1

Tengo la sensación de que está utilizando strncmp para evitar el desbordamiento del búfer; sin embargo, el mensaje ya está copiado en la memoria (es decir, el búfer de mensajes).Además, el prototipo

int strncmp (const char * str1, const char * str2, size_t num); 

indica que la función no tiene efectos secundarios (es decir, no cambia ya sea buffer de entrada) por lo que no debería haber ningún riesgo de que se sobreponen a la memoria intermedia y cambiar la memoria. (Este no es el caso de strcpy().)

Puede asegurarse de que la longitud del búfer de mensajes sea más larga que la cadena de comandos más larga. De esta forma, está seguro de que siempre está accediendo a la memoria que posee.

Además, si insiste en usar strncmp, puede almacenar su lista de comandos en una matriz y ordenarla de mayor a menor. Podría asociar cada cadena con una longitud (y posiblemente un puntero de función para ejecutar un controlador).

Finalmente, podría encontrar una versión C de lo que C++ llama un mapa o lo que Ruby o PHP llaman matrices asociativas. Esto le permite a la biblioteca manejar este árbol if-else de manera eficiente y correcta.

0

¿Su mensaje contiene solo uno de estos comandos, o una cadena de comandos seguida por espacios en blanco/abre-paréntesis/etc.?

Si es el primero, suelte strncmp y simplemente use strcmp.

Si es el último, simplemente marque isspace(message[strlen(command)]) o message[strlen(command)]=='(' o similar. (Nota: strlen(command) es una constante y probablemente debería escribirlo como tal, o usar una macro para obtenerlo del tamaño del literal de la cadena.)

+1

Uso: 'sizeof (" KeyWord ") - 1' - porque' sizeof() 'también cuenta el terminal nulo. Es una constante en tiempo de compilación, por supuesto. –

+0

Sí, supongo que debería haber mencionado todo eso. –

2

Una técnica es comparar los nombres más largos primero - ordene las pruebas (o la tabla que contiene las palabras clave) para que los nombres más largos precedan al más corto. Sin embargo, tomar su ejemplo:

GetPrimaryStatus 
GetPrimary 

Es posible que desee asegurarse de que GetPrimaryIgnition no se reconoce como GetPrimary. Entonces realmente necesita comparar utilizando la longitud de las dos cadenas más largas: el mensaje o la palabra clave.

Su estructura de datos que aquí podría ser:

static const struct 
{ 
    char *name; 
    size_t name_len; 
    int  retval; 
} Messages[] = 
{ 
    { "getPrimaryStatus", sizeof("getPrimaryStatus"), CMD_PRIMARYSTATUS }, 
    { "getPrimary",  sizeof("getPrimary"),  CMD_PRIMARY  }, 
    ... 
}; 

Puede a continuación, recorrer esta tabla para encontrar el comando correspondiente. Con algo de cuidado puede limitar el rango que debe observar. Tenga en cuenta que los valores sizeof() incluyen el NUL al final de la cadena. Esto es útil si puede cancelar el mensaje

Sin embargo, es mucho más simple si puede anular la palabra de comando en el mensaje, ya sea copiando el mensaje en alguna parte o modificando el mensaje in situ. A continuación, utilice strcmp() en lugar de strncmp(). Una búsqueda de prefijo único más corto es más difícil de codificar.

Una forma plausible de encontrar la palabra de comando es con strcspn() - asumiendo que todos los comandos son alfabéticos o alfanuméricos.

0

La única manera segura de usar strncmp para determinar si dos cadenas son iguales es verificar de antemano que las cadenas tienen la misma longitud:

/* len is a placeholder for whatever variable or function you use to get the length */ 
if ((len(a) == len(b)) && (strncmp(a, b, len(a)) == 0)) 
{ 
    /* Strings are equal */ 
} 

De lo contrario, se corresponderá con algo más largo o más corto que su comparación:

strncmp(a, "test", strlen("test")) coincide con "prueba", "prueba y un montón de otros caracteres", ect.

strncmp(a, "test", strlen(a)) coincide con "", "t", "te", "tes".

0

Utilice strcmp, pero también compare las longitudes de las dos cadenas. Si las longitudes son idénticas, entonces strcmp te dará el resultado que buscas.