/* * Javier Abellan, 20 Jun 2000 * * Funciones de lectura y escritura en sockets * * MODIFICACIONES: * 4 Septiembre 2003: Añadidas funciones para sockets UDP. */ #include #include #include #include #include #include #include #include #include "Socket.h" /* * Lee datos del socket. Supone que se le pasa un buffer con hueco * suficiente para los datos. Devuelve el numero de bytes leidos o * 0 si se cierra fichero o -1 si hay error. */ int Lee_Socket (int fd, char *Datos, int Longitud) { int Leido = 0; int Aux = 0; /* * Comprobacion de que los parametros de entrada son correctos */ if ((fd == -1) || (Datos == NULL) || (Longitud < 1)) return -1; /* * Mientras no hayamos leido todos los datos solicitados */ while (Leido < Longitud) { Aux = read (fd, Datos + Leido, Longitud - Leido); if (Aux > 0) { /* * Si hemos conseguido leer datos, incrementamos la variable * que contiene los datos leidos hasta el momento */ Leido = Leido + Aux; } else { /* * Si read devuelve 0, es que se ha cerrado el socket. Devolvemos * los caracteres leidos hasta ese momento */ if (Aux == 0) return Leido; if (Aux == -1) { /* * En caso de error, la variable errno nos indica el tipo * de error. * El error EINTR se produce si ha habido alguna * interrupcion del sistema antes de leer ningun dato. No * es un error realmente. * El error EGAIN significa que el socket no esta disponible * de momento, que lo intentemos dentro de un rato. * Ambos errores se tratan con una espera de 100 microsegundos * y se vuelve a intentar. * El resto de los posibles errores provocan que salgamos de * la funcion con error. */ switch (errno) { case EINTR: case EAGAIN: usleep (100); break; default: return -1; } } } } /* * Se devuelve el total de los caracteres leidos */ return Leido; } /* * Escribe dato en el socket cliente. Devuelve numero de bytes escritos, * o -1 si hay error. */ int Escribe_Socket (int fd, char *Datos, int Longitud) { int Escrito = 0; int Aux = 0; /* * Comprobacion de los parametros de entrada */ if ((fd == -1) || (Datos == NULL) || (Longitud < 1)) return -1; /* * Bucle hasta que hayamos escrito todos los caracteres que nos han * indicado. */ while (Escrito < Longitud) { Aux = write (fd, Datos + Escrito, Longitud - Escrito); if (Aux > 0) { /* * Si hemos conseguido escribir caracteres, se actualiza la * variable Escrito */ Escrito = Escrito + Aux; } else { /* * Si se ha cerrado el socket, devolvemos el numero de caracteres * leidos. * Si ha habido error, devolvemos -1 */ if (Aux == 0) return Escrito; else return -1; } } /* * Devolvemos el total de caracteres leidos */ return Escrito; } /** * Lee un mensaje de un socket udp. Los parámetros que se pasan son: * - Descriptor fd del socket del que se quiere leer. * - Estructura Remoto, en la que se devolverá los datos del que ha enviado el * mensaje que acabamos de leer. * - Longitud_Remoto de la estructura anterior. Debe pasarse relleno con el tamaño * de Remoto y se devolverá rellena con el tamaño de los datos en Remoto. * - Buffer de Datos donde se quiere que aparezca el mensaje. * - Longitud_Datos del buffer anterior. * * Devuelve el número de bytes leidos o -1 en caso de error. */ int Lee_Socket_Udp ( int fd, struct sockaddr *Remoto, socklen_t *Longitud_Remoto, char *Datos, int Longitud_Datos) { int Leido = 0; int Aux = 0; /* * Comprobacion de que los parametros de entrada son correctos */ if ((fd == -1) || (Datos == NULL) || (Longitud_Datos < 1) || (Remoto == NULL) || (Longitud_Remoto == NULL)) { return -1; } /* * Mientras no hayamos leido todos los datos solicitados */ while (Leido < Longitud_Datos) { Aux = recvfrom (fd, Datos + Leido, Longitud_Datos - Leido, 0, Remoto, Longitud_Remoto); if (Aux > 0) { /* * Si hemos conseguido leer datos, incrementamos la variable * que contiene los datos leidos hasta el momento */ Leido = Leido + Aux; } else { /* * Si read devuelve 0, es que se ha cerrado el socket. Devolvemos * los caracteres leidos hasta ese momento */ if (Aux == 0) return Leido; if (Aux == -1) { /* * En caso de error, la variable errno nos indica el tipo * de error. * El error EINTR se produce si ha habido alguna * interrupcion del sistema antes de leer ningun dato. No * es un error realmente. * El error EGAIN significa que el socket no esta disponible * de momento, que lo intentemos dentro de un rato. * Ambos errores se tratan con una espera de 100 microsegundos * y se vuelve a intentar. * El resto de los posibles errores provocan que salgamos de * la funcion con error. */ switch (errno) { case EINTR: case EAGAIN: usleep (100); break; default: return -1; } } } } /* * Se devuelve el total de los caracteres leidos */ return Leido; } /** * Escribe dato en el socket cliente. Devuelve numero de bytes escritos, * o -1 si hay error. * - fd es el descriptor del socket. * - Remoto es el destinatario del mensaje, a quién se lo queremos enviar. * - Longitud_Remoto es el tamaño de Remoto en bytes. * - Datos es el mensaje que queremos enviar. * - Longitud_Datos es el tamaño del mensaje en bytes. */ int Escribe_Socket_Udp (int fd, struct sockaddr *Remoto, socklen_t Longitud_Remoto, char *Datos, int Longitud_Datos) { int Escrito = 0; int Aux = 0; /* * Comprobacion de los parametros de entrada */ if ((fd == -1) || (Datos == NULL) || (Longitud_Datos < 1) || (Remoto == NULL) ) { return -1; } /* * Bucle hasta que hayamos escrito todos los caracteres que nos han * indicado. */ while (Escrito < Longitud_Datos) { Aux = sendto (fd, Datos + Escrito, Longitud_Datos - Escrito, 0, Remoto, Longitud_Remoto); if (Aux > 0) { /* * Si hemos conseguido escribir caracteres, se actualiza la * variable Escrito */ Escrito = Escrito + Aux; } else { /* * Si se ha cerrado el socket, devolvemos el numero de caracteres * leidos. * Si ha habido error, devolvemos -1 */ if (Aux == 0) return Escrito; else { return -1; } } } /* * Devolvemos el total de caracteres leidos */ return Escrito; } /** * Rellena una estructura sockaddr_in con los datos que se le pasan. Esta estrutura es * útil para el envio o recepción de mensajes por sockets Udp o para abrir conexiones. * Se le pasa el host. Puede ser NULL (para abrir socket servidor Udp o para recepción de * mensajes de cualquier host). * Se le pasa el servicio. Puede ser NULL (para abrir socket cliente Udp). * Se le pasa una estructura sockaddr_in que devolverá rellena. * Se le pasa una Longitud. Debe contener el tamaño de la estructura sockaddr_in y * devolverá el tamaño de la estructura una vez rellena. * Devuelve -1 en caso de error. */ int Dame_Direccion_Udp (char *Host, char *Servicio, struct sockaddr_in *Servidor, int *Longitud_Servidor) { struct servent *Puerto; struct hostent *Maquina; /* Comprobación de parámetros */ if (Servidor == NULL) return -1; /* Relleno del primer campo de la estructura */ Servidor->sin_family = AF_INET; /* Si nos han pasado un host ... */ if (Host != NULL) { /* ... obtenemos la dirección del host y la ponemos en la estructura */ Maquina = gethostbyname (Host); if (Maquina == NULL) return -1; Servidor->sin_addr.s_addr = ((struct in_addr *)(Maquina->h_addr))->s_addr; } else /* Si no nos han pasado un host, ponemos cualquier host. */ Servidor->sin_addr.s_addr = INADDR_ANY; /* Si servicio en NULL, hacemos que el puerto lo eliga el sistema operativo libremente.*/ if (Servicio == NULL) Servidor->sin_port = 0; else { /* Si el servicio no es NULL, lo obtenemos. */ Puerto = getservbyname (Servicio, "udp"); if (Puerto == NULL) return -1; Servidor->sin_port = Puerto->s_port; } return 0; }