Cliente TFTP

Buenas a todos,

Me presento, soy un estudiante de Ingenieria Tecnica en Informatica de Gestion y estoy haciendo pequeños homebrews para DS. Concretamente estoy haciendo un cliente de descargas TFTP. En principio funciona bien, el problema es que llega un momento que el servidor manda un paquete de datos a la DS y esta se queda esperando en el 'recvfrom' y no devuelve nada. Aqui os pongo el codigo de la funcion principal:

int main() {
    // Variables locales
    int _PAQ_DATOS_l = 0, num_bloque = 1, _PAQ_DATOS_i = 0, sainlen;
    char dato;
    struct sockaddr_in sain, saout;
    FILE *fichero;

    // BLOQUE DE INICIALIZACION
    //===========================================================================================

    //FIJAR CODIGO ACK
    _ACK[0] = 0;
    _ACK[1] = 4;
    //==============================

    // Initialize PAlib
    PA_Init();
    PA_InitVBL();

    //Inicializa el texto en la pantalla 1, fondo 0, y le da color blanco a las letras
    PA_InitText(1, 0);
    PA_SetTextCol(1, 31, 31, 31);

    //Inicializa libreria FAT
    fatInitDefault();

    // Inicializa Wifi
    PA_OutputText(1, 9, 10, "Iniciando Wifi");
    PA_InitWifi();
    PA_ConnectWifiWFC(); // Se conecta al primer punto de acceso configurado en el WFC
    PA_OutputText(1, 9, 10, "              ");

    //Inicializacion del teclado
    PA_InitKeyboard(2);

    //Creaciond el socket UDP
    _SOCKET = socket(AF_INET, SOCK_DGRAM, 0);

    //Fijar los datos del cliente
    saout.sin_family = sain.sin_family = AF_INET;
    saout.sin_port = htons(69);
    sain.sin_port = htons(1024);
    sain.sin_addr.s_addr = INADDR_ANY;
    //PedirIpUsuario();
    saout.sin_addr.s_addr = inet_addr("192.168.1.101");
    sainlen = sizeof (sain);

    //No se lo que hace esto pero es necesario
    bind(_SOCKET, (struct sockaddr *) & sain, sizeof (sain));

    //=====================================================================================

    PA_OutputText(1, 1, 2, "%c5IP servidor: %s", _IP_SERVIDOR);
    PedirUrlUsuario();

    //Creacion del fichero
    fichero = fopen(_URL, "wb");

    PA_OutputText(1, 1, 4, "                                ");
    PA_OutputText(1, 1, 4, "%c5Archivo: %s", _URL);
    PA_OutputText(1, 1, 5, "                                ");
    PA_OutputText(1, 1, 7, "                                ");

    PA_OutputText(1, 1, 6, "%c5Estado:");

    //Construccion y envio del RRQ
    ConstruirRRQ();

    sendto(_SOCKET, _RRQ, _RRQ_i, 0, (struct sockaddr*) & saout, sizeof (saout));
    PA_OutputText(1, 4, 8, "%c5Enviado RRQ");

    //Recepcion del primer paquete
    _PAQ_DATOS_l = recvfrom(_SOCKET, _PAQ_DATOS, DATA_SIZE, 0, (struct sockaddr*) & sain, &sainlen);

    //Reestablecer el puerto de destino al generado aleatoriamente por el servidor
    saout.sin_port = sain.sin_port;

    while (_PAQ_DATOS_l == DATA_SIZE) {

        if (ExtraerCodigoOperacion() == 5) {

            GestionarError(fichero);
            break;
        } else if (ExtraerCodigoOperacion() != 3) {
            PA_OutputText(1,1,14,"Operacion desconocida         ");
            do {
                _PAQ_DATOS_l = recvfrom(_SOCKET, _PAQ_DATOS, DATA_SIZE, 0, (struct sockaddr *) & sain, &sainlen);
            } while (ExtraerCodigoOperacion() != 3 || ExtraerCodigoOperacion() != 5);
            break;
        } else if (ExtraerCodigoBloque() != num_bloque && ExtraerCodigoOperacion() == 3) {

            GestionarBloque(fichero, num_bloque);
            break;
        } else if (ExtraerCodigoBloque() == num_bloque && ExtraerCodigoOperacion() == 3) {

            //Extraccion y guardado de los datos
            for (_PAQ_DATOS_i = 4; _PAQ_DATOS_i <= _PAQ_DATOS_l - 1; _PAQ_DATOS_i++) {
                dato = _PAQ_DATOS[_PAQ_DATOS_i];
                fwrite(&dato, 1, sizeof (dato), fichero);
                fflush(fichero);
            }
            PA_OutputText(1, 2, 8, "%c5Datos guardados            ");

            CrearACK(num_bloque);
           
            sendto(_SOCKET, _ACK, _ACK_i, 0, (struct sockaddr*) & saout, sizeof (saout));


            _PAQ_DATOS_l = recvfrom(_SOCKET, _PAQ_DATOS, DATA_SIZE, 0, (struct sockaddr *) & sain, &sainlen);

            while (ExtraerCodigoBloque() != num_bloque + 1) { //Aun no ha llegado un paquete nuevo, la DS es mas rapida?
                _PAQ_DATOS_l = recvfrom(_SOCKET, _PAQ_DATOS, DATA_SIZE, 0, (struct sockaddr *) & sain, &sainlen);
            }
            PA_OutputText(1, 2, 8, "%c5Recibido paquete, bloque: %d", ExtraerCodigoBloque());
            num_bloque++;
            if (num_bloque == 65536) num_bloque = 0;
        }
    }
    // ULTIMO PAQUETE

    //Comprobacion del codigo del bloque
    if (ExtraerCodigoOperacion() == 5) {
        GestionarError(fichero);
    } else if (ExtraerCodigoOperacion() != 3) {
        do {
            _PAQ_DATOS_l = recvfrom(_SOCKET, _PAQ_DATOS, DATA_SIZE, 0, (struct sockaddr *) & sain, &sainlen);
        } while (sain.sin_addr.s_addr != saout.sin_addr.s_addr);
    } else if (ExtraerCodigoBloque() != num_bloque) {
        GestionarBloque(fichero, num_bloque);
    } else {

        //Extraccion de los datos
        for (_PAQ_DATOS_i = 4; _PAQ_DATOS_i <= _PAQ_DATOS_l - 1; _PAQ_DATOS_i++) {
            dato = _PAQ_DATOS[_PAQ_DATOS_i];
            fwrite(&dato, 1, 1, fichero);
        }
        //Guardar los datos
        PA_OutputText(1, 4, 8, "%c5Guardando %d bytes de datos", _PAQ_DATOS_l);
        fflush(fichero);

        //Creacion del ACK correspondiente
        CrearACK(num_bloque);

        //Envio ACK
        PA_OutputText(1, 2, 8, "%c5Enviado ACK para bloque %d     ", num_bloque);
        sendto(_SOCKET, _ACK, _ACK_i, 0, (struct sockaddr*) & saout, sizeof (saout));
        PA_OutputText(1, 4, 14, "%c2FIN DE LA TRANSFERENCIA");
    }
    //Cierre del fd del fichero abierto
    fclose(fichero);

    //Fin del programa
    return 0;
}


A ver si alguno de vosotros me puede hechar un cable.

Gracias de antemano.

(17/02/2010) EDIT:

El programa implementa la RFC1350 (Trivial File Transfer Protocol, con Stop & Wait), este protocolo especifica que el cliente tiene que usar un solo puerto para enviar y recibir datos, ya que el servidor los enviara al socket que le haya enviado la peticion. Lo que me pasa a mi es que el programa funciona correctamente durante un tiempo indeterminado en el que el cliente (DS) recibe los paquetes perfectamente, pero llega un momento que el servidor envia un paquete de datos y la DS se queda clavada en el "recvrfrom", pero esto puede ser en el paquete 30, en el 800 o en el 9000, es bastante aleatorio. He probado usando la funcion"select" para que espere hasta que haya una modificacion en el socket y entonces siga, y no he conseguido nada.

No creo que este comendome toda la RAM ni colapsando la CPU, por otra parte tampoco es que el servidor envie el paquete y la DS no lo lea (porque para eso es Stop & Wait).
Pregunta por AntonioND o por Plata [ginyo]
Rigle escribió:Pregunta por AntonioND o por Plata [ginyo]

AntonioND nunca ha tenido que usar funciones de red así que no tiene ni la más remota idea de cómo se usan.
Plata las habra utilizado?
ANTONIOND escribió:
Rigle escribió:Pregunta por AntonioND o por Plata [ginyo]

AntonioND nunca ha tenido que usar funciones de red así que no tiene ni la más remota idea de cómo se usan.

Entonces no hay ninguna oportunidad de sobrevivir [mad]
Haber, te explico, yo e usado funciones de redes y tal, pero en pc y ace algun tiempo, con palib nunca he rabajado con redes. No estoy seguro de donde esta el error(tampoco se si el primer paquete que intentas escuchar lo pillas, doy por supuesto que no :S). Por lo que veo, usas el mismo socket para enviar y recibir datos. Creo que hay está tu problema. Al hacer bind a un socket, asocias dicho socket a una dirección local. Lo primero, no se si podras hacer que un socket pase de enviar datos a recibirlos como si nada. Lo segundo, como ya te he dicho, no se como va en palib, pero en pc, para recibir datos por un socket, tienes primero que ponerlo en escucha, luego aceptar la conexion del puerto y por ultimo ya recibir los datos. Investiga un poco esto. De todas formas ya te digo, prueba a usar dos sockets diferentes, que con eso no creo que tengas problem alguno. Si aun asi sigue sin funcionar (ya que te repito que nunca he tocado esto en ds y en pc no profundice mucho), preguntale a mikau. El si que sabe bastante de como funcionan redes en ds y demas usando palib.

Hechale un vistazo a este link, es para pc, pero igual te sirve, de ahi aprendi a empezar con redes:
https://www.underground.org.mx/index.php?topic=20773.0

salu2
Gracias Plata por tu ayuda, me mirare el tutorial que me has pasado, de todas formas he puesto mas datos del funcionamiento programa (xq el post lo escribi desde la uni y no tenia mucho tiempo :p ).
rubsancas escribió:Gracias Plata por tu ayuda, me mirare el tutorial que me has pasado, de todas formas he puesto mas datos del funcionamiento programa (xq el post lo escribi desde la uni y no tenia mucho tiempo :p ).

Has pensado la posibilidad de que recibas una cantidad de datos muy grande? Porque que pete aleatoriamente es raro. Mas que eso ya no se que decirte, porque la verdad no se mucho mas de redes XD Lo mas que hice fue un chat multi cliente (varios clientes que se conectan al mismo server), pero todo eso usando hilos y un socket por cada conexión en pc.

salu2
No creo que sea eso, porque el servidor siempre envia la misma cantidad de datos (el servidor esta hecho en Java por mí). Por otro lado, el "recvfrom" hacia cosas muy raras, como si en dos iteraciones devolviera los mismos datos, por eso controlo que lo que se reciba no tenga el mismo codigo de bloque.

(19/2/2010) EDIT:

Me acorde que de tanto me aparecia un paquete mas grande, asi que he probado ha aumentar el buffer a mas de 10000 bytes y al prbarlo se ha quedado colgado en el paquete 8100 y pico, asi que he pasado a probar con el "malloc" en ves de usar unaarray pero se sigue quedando colgado, y aunque aumente el tamaño reservado en memoria se queda clavado mas o menos por el paquete 3000 y pico.
rubsancas escribió:No creo que sea eso, porque el servidor siempre envia la misma cantidad de datos (el servidor esta hecho en Java por mí). Por otro lado, el "recvfrom" hacia cosas muy raras, como si en dos iteraciones devolviera los mismos datos, por eso controlo que lo que se reciba no tenga el mismo codigo de bloque.

(19/2/2010) EDIT:

Me acorde que de tanto me aparecia un paquete mas grande, asi que he probado ha aumentar el buffer a mas de 10000 bytes y al prbarlo se ha quedado colgado en el paquete 8100 y pico, asi que he pasado a probar con el "malloc" en ves de usar unaarray pero se sigue quedando colgado, y aunque aumente el tamaño reservado en memoria se queda clavado mas o menos por el paquete 3000 y pico.


Realmente, palib no es la libreria mejor programada de la historia, si hace cosas raras, te aconsejo que uses libnds y dswifi y compiles tu programa sin usar palib. Asi te aseguras de que no es la lib.

salu2
Plata escribió:
rubsancas escribió:No creo que sea eso, porque el servidor siempre envia la misma cantidad de datos (el servidor esta hecho en Java por mí). Por otro lado, el "recvfrom" hacia cosas muy raras, como si en dos iteraciones devolviera los mismos datos, por eso controlo que lo que se reciba no tenga el mismo codigo de bloque.

(19/2/2010) EDIT:

Me acorde que de tanto me aparecia un paquete mas grande, asi que he probado ha aumentar el buffer a mas de 10000 bytes y al prbarlo se ha quedado colgado en el paquete 8100 y pico, asi que he pasado a probar con el "malloc" en ves de usar unaarray pero se sigue quedando colgado, y aunque aumente el tamaño reservado en memoria se queda clavado mas o menos por el paquete 3000 y pico.


Realmente, palib no es la libreria mejor programada de la historia, si hace cosas raras, te aconsejo que uses libnds y dswifi y compiles tu programa sin usar palib. Asi te aseguras de que no es la lib.

salu2

Las únicas funciones de PAlib de wifi son wrappers de las de dswifi, lo que acabas de decir es una soberana tontería... :-| Además, las únicas funciones de wifi de PAlib que veo ahí son PA_InitWifi y PA_ConnectWifiWFC.
Bueno, he estado haciendo mas pruebas y por fin he descubierto cual es el problema, no es el codigo, ni la consola, ni el servidor,....paquetes perdidos [ayay] el problema es tan simple que hasta que no se me ocurrio no lo pude descubrir. Se me acudio hacer un clon del servidor y modificarlo para que su funcion fuera enviar un paquete a la DS directamente, asi que cuando la DS paró de transmitir paquetes y el servidor dio timeout, ejecute el otro programa, y en la DS aparecio un mensaje que programe para cuando llegara un paquete no esperado.

Me doy de cabeza contra la pared por no haber descubierto semejante tonteria [tomaaa]. Ahora solo me queda modificar el programa para que pueda reenviar paquetes despues de un timeout (esa es otra, a ver como lo hago).

Gracias a todos por vuestra ayuda.
11 respuestas