Construir char[] mediante variables dinámicas

Si eres principiante y tienes alguna consulta entra en este foro.
Responder
Mensaje
Autor
ugeltroglodita
Mensajes: 4
Registrado: 15/02/2019 3:44 am

Construir char[] mediante variables dinámicas

#1 Mensaje por ugeltroglodita » 24/10/2019 6:53 am

Buenas, estoy realizando un programa que, entre otras cosas, mantiene una comunicación con un servidor mediante envío y recepción de tramas en formato JSON. Tengo hecha la parte de recibir las tramas y funciona perfectamente, recibo todas las tramas sin problemas, y parseo sus valores y los guardo en las estructuras y variables correspondientes. Ahora estoy implementando la parte de enviar trama al servidor, para ello tengo una función que construye la trama a enviar, y otra función para enviar dicha trama al servidor.

Una vez situados, el problema lo tengo en la función que genera la trama. En principio la veo bien, puesto que si printo el contenido de la trama por pantalla sale lo esperado. Pero, al ejecutar esta función, no sé el motivo, la recepción de tramas deja de funcionar. Para mí es incomprensible puesto que una cosa no debería tener influencia en la otra. Solo hago lecturas de variables globales (para construir la trama), y creo una variable local que es la que paso a la función para enviar trama al servidor (función comentada acutalmente). Dato importante: he observado que si la trama que genero es menor o igual a 80 bytes el programa funciona normalmente, pero en el momento que genero una trama mayor a 80 bytes la recepción de tramas deja de funcionar SIEMPRE (básicamente si comento todos los sprintf menos los 3-4 primeros funciona bien, a la que dejo alguno más ya no va). No sé si puede haber algún problema en mi manera de generar la trama, que haga que modifique valores más allá del tamaño del char[] destinado a contener la trama. Pego código por si me pudierais indicar si veis algo anormal, porque la verdad es que no entiendo cómo el uso de esta función puede influir en el funcionamiento de otras partes del programa.

Aquí la estructura de donde sacamos los datos para generar la trama:

Código: Seleccionar todo

typedef struct 
{
    char usu[10]; // Usuario
    char pas[10]; // Password
    char Coc[50]; // Código Conexión
    char sCo[10]; // Código de parada
    float tIn; // Temperatura interior
    BYTE cPa;// = false; // Estado pantalla
    //...algunos parámetros más, que omito porque sería redundar en lo mismo
}DatosEnviadosSGIP;
Aquí la declaración de la variable global que usa esta estructura:

Código: Seleccionar todo

DatosEnviadosSGIP gDatosEnviados;


Aquí la función que envia el código

Código: Seleccionar todo

bool enviarDatosConFuncionUnificada(){

	//Construir trama paso a paso
	BYTE jsonEnvio[800] = "{";
	
	sprintf(jsonEnvio,"%s\"usu\":\"%s\",",jsonEnvio,gDatosEnviados.usu);
	sprintf(jsonEnvio,"%s\"pas\":\"%s\",",jsonEnvio,gDatosEnviados.pas);
	sprintf(jsonEnvio,"%s\"coC\":\"%s\",",jsonEnvio,gDatosEnviados.Coc);
	sprintf(jsonEnvio,"%s\"sCo\":\"%s\",",jsonEnvio,gDatosEnviados.sCo);
	sprintf(jsonEnvio,"%s\"tIn\":%f,",jsonEnvio,gDatosEnviados.tIn);
	sprintf(globalJsonEnvio,"%s\"cPa\":%s,",globalJsonEnvio,gDatosEnviados.cPa ? "true" : "false");
	
	//...
	//Varios campos más a añadir, que omito porque sería redundar en lo mismo

	//Hay que quitar el último carácter, que será una coma, fruto de la construcción anterior
	jsonEnvio[strlen(jsonEnvio)-1] = '\0';
		
	strcat(jsonEnvio,"}");
	int tamanyoJsonEnvio = strlen(jsonEnvio);
	
	//Construir URL de petición a servidor		
	char cmd_to_POST[72];
	uint8_t aux_cmd_len = sprintf(cmd_to_POST,"%s/apistop/%s", configParams_tmp.url, configParams_tmp.url_code);
	
	sprintf(HiMsg,"Enviando POST a la URL %s \r\n",cmd_to_POST);
	print(HiMsg);
	sprintf(HiMsg,"aux_cmd_len tiene = %i bytes\r\n",aux_cmd_len);
	print(HiMsg);
	sprintf(HiMsg,"Enviando %i bytes de JSON:\r\n%s\r\n",tamanyoJsonEnvio,jsonEnvio);
	print(HiMsg);
	
	return false;//return BG96_post_unified(cmd_to_POST,aux_cmd_len,jsonEnvio,tamanyoJsonEnvio);
}
Además de esto, también he intentado 2 variaciones:

1. Sustituir la variable local jsonEnvio por una global, que limpio en cada invocación a la función, por si de esta manera no hago trabajar tanto al dispositivo a la hora de asignar/liberar memoria:

Código: Seleccionar todo

BYTE globalJsonEnvio[800];
bool enviarDatosConFuncionUnificadaGlobal(){

      for (int i=0;i<800;i++) globalJsonEnvio[i] = '\0'; //Limpiamos la trama de la invocación anterior
      globalJsonEnvio[0] = '{';
      sprintf(globalJsonEnvio,"%s\"usu\":\"%s\",",globalJsonEnvio,gDatosEnviados.usu);
      //...
      //el resto es todo igual que en la otra, cambiando jsonEnvio por globalJsonEnvio
}     
2. Construir la cadena mediante strcat en lugar de sprintf, tal que así:

Código: Seleccionar todo

bool enviarDatosConFuncionUnificadaStrCat(){
	BYTE jsonEnvio[800] = "{";
	BYTE auxCat[50];
	
	sprintf(auxCat,"\"usu\":\"%s\",\0",gDatosEnviados.usu);
	strcat(jsonEnvio,auxCat);
	sprintf(auxCat,"\"pas\":\"%s\",\0",gDatosEnviados.pas);
	strcat(jsonEnvio,auxCat);
	sprintf(auxCat,"\"coC\":\"%s\",\0",gDatosEnviados.Coc);
	strcat(jsonEnvio,auxCat);
	sprintf(auxCat,"\"sCo\":\"%s\",\0",gDatosEnviados.sCo);
	strcat(jsonEnvio,auxCat);
	
	jsonEnvio[strlen(globalJsonEnvio)-1] = '\0';
	strcat(jsonEnvio,"}");
	
	//...
	//el resto es todo igual que en la otra, cambiando jsonEnvio por globalJsonEnvio
	
}
Mismo resultado que en los casos anteriores. En cuanto se printa por pantalla que tamanyoJsonEnvio es mayor a 80 bytes deja de funcionar la recepción de tramas del servidor.

¿Alguna idea?

Avatar de Usuario
kiko66
Mensajes: 41
Registrado: 11/10/2019 10:51 am

Re: Construir char[] mediante variables dinámicas

#2 Mensaje por kiko66 » 26/10/2019 7:15 am

hola troglodita :)

veras yo no llego a tanto :oops:

bueno has elegido el foro para principiantes :roll:

quizas en otro tengas mas suerte :wink:

chao chao

mollok
Mensajes: 522
Registrado: 30/01/2018 9:47 am
Ubicación: Mallorca, España

Re: Construir char[] mediante variables dinámicas

#3 Mensaje por mollok » 27/10/2019 1:33 am

Lo de construir cadenas puedes probar se usar un único sprintf aprovechando que dos cadenas seguidas cuenta como concatenación:

Código: Seleccionar todo

sprintf(jsonEnvio,
    "{"
        "\"usu\":\"%s\","
        "\"pas\":\"%s\","
        "\"coC\":\"%s\","
        "\"sCo\":\"%s\","
        "\"tIn\":%f"
    "}"
    , jsogDatosEnviados.usu
    , gDatosEnviados.pas
    , gDatosEnviados.Coc
    , gDatosEnviados.sCo
    , gDatosEnviados.tIn
);
Así queda un poco más claro.

En cuanto a los 80 bytes puede que sea un límite puesto por el servidor. Si no puedes tocarlo intenta reducir tamaño de la trama: identificadores con menos letras, limita la precisión de los float al mínimo imprescindible con %.2f (por ejemplo)
O crea una función en el servidor que, como TCP, haga un control de tramas: en plan:
C: Nueva comunicación de tres tramas.
S: Ok, tu id es 1. Timeout 1s.
C: id 1, trama 1, tamanyo 80 bytes, [datos de la trama]
S: id 1, trama 1, Ok
Cliente envía tramas 2 y 3 del mismo modo.

El servidor, a medida que las recibe y están ok va concatenado los datos.

Sí una trama no es correcta o no llega el servidor la vuelve a pedir.

Sí durante la espera de una trama se supera el timeout el servidor desecha la trama y cierra la comunicación dando un mensaje de error al cliente.

Es un ejemplo, lo puedes hacer a tu manera.

La cosa es que si estás limitado a 80 bytes deberás buscar cómo reducir el tamaño o cómo trocear el mensaje.
while(is_alive(yourself)) {
    make_true(yourself, yourdreams);
}

ugeltroglodita
Mensajes: 4
Registrado: 15/02/2019 3:44 am

Re: Construir char[] mediante variables dinámicas

#4 Mensaje por ugeltroglodita » 07/11/2019 2:16 am

Gracias por tu ayuda mollok y perdón por la tardanza en contestar (he tenido otros marrones en el trabajo y este desarrollo se ha quedado en segundo plano).

O no me expliqué bien o no me entendiste, el problema no es la comunicación con el servidor, por dos motivos:
- Esta comunicación está testada y funciona perfectamente en programa JAVA (del que este es copia, para adaptarlo a otro dispositivo)
- En el código que puse no se envía nada a servidor, simplemente se construye la cadena que HABRÍA que mandar al servidor, y solo con eso el programa ya deja de funcionar.

Al final el problema es el que me suponía, aunque estaba mirando en dirección incorrecta. Lo explico aquí por si alguien se encuentra con algo parecido y le puede resultar de utilidad.

Mi única explicación plausible al problema era que estuviera construyendo mal la cadena y estuviese abarcando más memoria de la que había reservado para la variable destinada a contener la cadena de envío al servidor, y que al alterar otras zonas de memoria estuviese afectando a otras partes del programa. Y efectivamente, así ha sido. La cadena la he generado bien, pero luego al printar por pantalla me ayudo de una variable global que es HiMsg, en esta instrucción:

Código: Seleccionar todo

sprintf(HiMsg,"Enviando %i bytes de JSON:\r\n%s\r\n",tamanyoJsonEnvio,jsonEnvio);
print(HiMsg);
Pues bien, la declaración de HiMsg es esta:

Código: Seleccionar todo

char HiMsg[80];
Así que está claro que cualquier cadena de más de 80 caracteres que copiemos ahí alterará zonas de memoria que no sabemos qué contienen, y podemos afectar al funcionamiento del programa.

Responder

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 8 invitados