sisoputnfrba / foro

Foro de consultas para el trabajo práctico
148 stars 7 forks source link

Problema en la conexión entre el servidor y el cliente: no se registran los paquetes en el logger. #3493

Closed GabrielBorre closed 5 months ago

GabrielBorre commented 5 months ago
Buenas! Estábamos intentando conectar el cliente (kernel) con el servidor (memoria) y nos surgió un problema. Nosotros estábamos esperando que una vez que el cliente enviara su paquete al servidor, este lo recibiera y mostrara las líneas compuestas por el paquete por consola. Sin embargo, ocurre lo siguiente: por un lado, el cliente (kernel) permite mostrar por consola las líneas que le vamos pasando, pero por otro, entendemos que el servidor no recibe correctamente el paquete enviado por el cliente. No modificamos ninguna de las funciones provistas en el TP0, ni agregamos parámetros a las mismas. Adjunto fotos de: 1) Lo que se muestra en la consola del cliente cuando intenta enviar un paquete (las líneas se muestrar correctamente por consola) ![imagen 1](https://github.com/sisoputnfrba/foro/assets/83511268/68d96fa7-cc24-4543-9aa3-2ebc1785bef8) 2)la pila de llamadas que el archivo main.c del servidor realiza para recibir el paquete enviado por el cliente. ![imagen 2](https://github.com/sisoputnfrba/foro/assets/83511268/80bee643-095f-4022-a906-9bb57628a461) 3)la primera función llamada por el archivo main.c del servidor (es una función que tiene el mismo comportamiento que el cuerpo del server.c en el tp0, pero la abstrajimos como función en otro archivo porque sabemos que varios módulos van a llamar a la misma). ![imagen 3](https://github.com/sisoputnfrba/foro/assets/83511268/c086f540-e207-477a-bc93-c573522e16b3) 4)La última llamada de la pila que genera una excepción (segmentation fault). En esta última foto también se visualiza lo que se muestra por consola desde el punto de vista del servidor. ![imagen 4](https://github.com/sisoputnfrba/foro/assets/83511268/6ed3dfb0-acf6-4087-b816-a78358dfa773) Asimismo, les dejo el link del github por si desean revisar el contenido de cada función: https://github.com/sisoputnfrba/tp-2024-1c-Ope-ativos Desde ya, muchas gracias! Saludos! ### 🔎 Búsqueda en foros Estuve revisando los issues y, si bien veo que existieron problemas con este tipo de conexiones, ninguno expone este comportamiento en particular. ### 📝 Código relevante

🐛 Cómo reproducir el error

💻 Logs

iago64 commented 5 months ago

Buenas! Cómo va?

Si te fijas en la última captura lo que te explota es que no le esta llegando un t_log * válido a la función isEnableLevelInLogger(), de hecho le llega la dirección 0x2 que no es una dirección de tu proceso ni de onda, fijate de utilizando el debugger, pararte justo antes de llamar a cualquier función de log_ a ver si le estas pasando bien la referencia.

Saludos.-

GabrielBorre commented 5 months ago

Buen día Damián! Muchas gracias por tu respuesta! Intentaremos solucionarlo por donde nos decís. Saludos y buen Domingo.

MariaSolGaraventa commented 5 months ago

Hola Damián! Soy del mismo grupo que Gabriel. Te comento nuestras sospechas.

De la pila de funciones que te mandamos, hasta que se llama a int servidor(const char *puerto, t_log *logger) y antes de entrar a la función list_iterate(lista, (void*) iterator) está todo bien, las variables son estas: image

El problema es que cuando entra en esa función, pierde el linkeo del logger. Nosotros sí modificamos los parámetros de iterator justamente para que le llegue el logger que nosotros queríamos, puesto que cada servidor tendría su propio logger (entonces, debería ser pasado por parámetro de mano en mano), quedando iterator de la siguiente manera:

void iterator(char* value, t_log *logger) {
    log_info(logger,"%s", value);
}

Siguiendo con nuestra sospecha, list_iterate(lista, (void*) iterator) llama únicamente a la firma de la función y pierde el parámetro logger. No te voy a mentir que no entendemos mucho qué hace list_iterate según su código, sí según su nombre.

void list_iterate(t_list* self, void(*closure)(void*)) {
    t_link_element **indirect = &self->head;
    while ((*indirect) != NULL) {
        closure((*indirect)->data);
        indirect = &(*indirect)->next;
    }
}

Vemos que list_iterate recibe una lista y ¿una función anónima que recibe cualquier parámetro? A partir de acá, las variables quedan así:

image

image

Y como vemos, se desvinculó el logger. ¿Qué podríamos hacer para que cada servidor tenga su propio logger y no tengamos que hacer un pasamanos sin modificar las funciones que ya nos dan? ¿o deberíamos modificarlas de alguna forma en específico?

Muchas gracias, espero que esto sirva para ayudanos a resolver el problema. Buen domingo a todos!!

iago64 commented 5 months ago

Buenas! Cómo va?

Justamente lo que mencionas de list_iterate() es donde le pifiaron al análisis, lo que hace en la línea closure((*indirect)->data); es pasarle el contenido de la lista, es decir, lo que uds tienen que recibe 2 parametros, solo les manda el primero y el segundo bueno... como diría un español a tomar por saco.

Este problema de no poder tener 2 parámetros en la función que se aplica a cada elemento de la lista en list_iterate() se resuelve de 2 maneras más o menos "simples"

Manera 1: Arman un elemento de la lista más complejo que tenga más cosas. Esto lo pueden ver en los unit tests de las listas donde el list_iterate() opera sobre un t_person *, en lugar de un char *.

Manera 2: Tienen una variable global con el valor que necesitan, que en su caso siendo que es un logger, es un poco la salida más fácil, teniendo en cuenta que ese logger lo van a llamar de muchos lados, capaz es más fácil tener una variable global y que desde cualquier lado le puedan pegar.

Saludos.-

RaniAgus commented 5 months ago

¡Buenas! Solo me sumo para agregar una manera 3: usar nested functions

Entonces pueden armar una función que reciba una lista y un logger, y que adentro de ella se llame a list_iterate pasándole como parámetro una nested function.

Saludos

MariaSolGaraventa commented 5 months ago

Buenas Damián y Agus! Estuvimos barajando las opciones:

De la manera 1 sentimos que sumamos mucha complejidad al código, y que nos vamos a complicar mucho 😩.

De la manera 2, si bien es mucho más factible, tenemos abstraída la lógica del servidor en un archivo utils/src/server.c, llamando a la función servidor(puerto_propio, logger) (está en utils/src/server.c) desde el main de memoria. Por esto, dedujimos que una variable global tampoco nos serviría porque al toque cambiamos de archivo, y definirla como extern haría o que tengamos un sólo logger o que tengamos que definir las funciones 3 o 4 veces para cada módulo, y para eso podemos hacerlo directamente teniendo la lógica del servidor en cada archivo.

La manera 3 nos pareció interesante puesto que quizás también nos servía en futuras ocasiones, pero creo que no la estamos implementando del todo bien ya que nos tira segmentation fault en una funcion de las commons de strings void _string_append_with_format_list(const char* format, char** original, va_list arguments)... (un dolor de cabeza todo). Adjunto código de cómo lo implementamos (con nombres genéricos porque era para probar):

En utils/src/server.c:

void iterator(char* value, t_log *logger) {
    log_info(logger,"%s", value);
}

int servidor(const char *puerto, t_log *logger) {

    int server_fd = iniciar_servidor(puerto, logger);
    log_info(logger, "Servidor listo para recibir al cliente");
    int cliente_fd = esperar_cliente(server_fd, logger);

    t_list* lista;
    while (1) {
        int cod_op = recibir_operacion(cliente_fd);
        switch (cod_op) {
        //...
        case PAQUETE:
            lista = recibir_paquete(cliente_fd);
            log_info(logger, "Me llegaron los siguientes valores:\n");

            **funcion(lista, logger);**

            break;
             //...
}

void funcion(t_list *lista, t_log *logger) {
    char* value;
    void _funcion_adaptadora(t_list *lista) {
        iterator(value, logger);
    }
    list_iterate(lista, (void*) _funcion_adaptadora);
}

Los parámetros de list_iterate y de iterator, después de la definición _funcion_adaptadora, no se pintan 😩, así que ahí está el primer parámetro de que no funciona (y el más importante obvio!! jajaj). Lo segundo (y mucho menos importante (nótese el sarcasmo)) es la exceción de segmentation fault:

image

Ya decidimos que si esto no mejora, vamos a destruir la abstracción y bueno, se repetirá lógica pero al menos va a funcionar (elegimos creer).

Muchas gracias 💙

RaniAgus commented 5 months ago

Holi! Justo en la funcion están ocurriendo un par de cosas:

void funcion(t_list *lista, t_log *logger) {
    char* value; // (1)
    void _funcion_adaptadora(t_list *lista) { // (3)
        iterator(value, logger); // (2)
    }
    list_iterate(lista, (void*) _funcion_adaptadora);
}
  1. La variable value que declararon ahí está sin inicializar
  2. Esa variable se la están pasando a iterator, con lo cual de ahí seguramente venga el segfault.
  3. Si utilizan el debugger verán que este parámetro de la _funcion_adaptadora, no es la lista en sí, sino cada elemento de la misma. O sea, de ahí vendría el char* value que esperan pasarle a iterator :)
sanchezflorencia commented 5 months ago

Buenas a todos! Soy tambien parte del grupo. Logre que el servidor y el cliente se conecten con la solucion que plantearon de nested function, dejo constancia de lo que hice por si quieren corregir o por si a alguien le llega a servir. Construi la funcion de la siguiente manera para que funcione (valga la redundancia jaja)

void funcion(t_list *lista, t_log *logger){
    void _iterator(char *value){
        log_info(logger,"%s", value);
    }
    list_iterate(lista,(void*) _iterator);
}

y dentro de la funcion principal queda asi:

int servidor(const char *puerto, t_log *logger) {

    int server_fd = iniciar_servidor(puerto, logger);
    log_info(logger, "Servidor listo para recibir al cliente");
    int cliente_fd = esperar_cliente(server_fd, logger);

    t_list* lista;
    while (1) {
        int cod_op = recibir_operacion(cliente_fd);
        switch (cod_op) {
        case MENSAJE:
            recibir_mensaje(cliente_fd, logger);
            break;
        case PAQUETE:
            lista = recibir_paquete(cliente_fd);
            log_info(logger, "Me llegaron los siguientes valores:\n");
            funcion(lista,logger);
            break;
        case -1:
            log_error(logger, "El cliente se desconectó. Terminando servidor");
            return EXIT_FAILURE;
        default:
            log_warning(logger,"Operacion desconocida");
            break;
        }
    }
    return EXIT_SUCCESS;
}

muchas gracias a todos, me sirvio un montonazo el video de ejemplo jajaja saludos!

GabrielBorre commented 5 months ago

Muchísimas gracias Damián y Agus por la gran ayuda!

Cierro el issue.

Saludos y buena semana!