La semana pasada, nuestro profesor de Sistemas Distribuidos en la facultad, nos dió como trabajo práctico desarrollar un pequeño servidor de archivos en lenguaje C utilizando Sockets en Linux.
Después de investigar un buen rato, terminamos desarrollando un servidor (que claro está, corre en Linux con un puerto definido en el código) y un cliente (que también tiene definido el puerto en el código).
El código fuente a continuación:
servidor.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define SERVER_PORT 7000 #define BUF_SIZE 4096 #define QUEQUE_SIZE 10 int main(int argc, char *argv[]) { int s, b, l, fd, sa, bytes, on = 1; char buf[BUF_SIZE]; struct sockaddr_in channel; // Construye la estructura de la direccion para enlazar el socket memset(&channel, 0, sizeof(channel)); channel.sin_family = AF_INET; channel.sin_addr.s_addr = htonl(INADDR_ANY); channel.sin_port = htons(SERVER_PORT); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if ( s < 0 ) { printf(”ERROR: error de socket\n”); exit(0); } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)); b = bind(s, (struct sockaddr *) &channel, sizeof(channel)); if ( b < 0 ) { printf(”ERROR: error de bind\n”); exit(0); } l = listen(s, QUEQUE_SIZE); if ( l < 0 ) { printf(”ERROR: error de listen\n”); exit(0); } while (1) { sa = accept(s, 0, 0); if (sa < 0) { printf(”ERROR: error de accept\n”); exit(0); } read(sa, buf, BUF_SIZE); fd = open(buf, O_RDONLY); if (fd < 0) { printf(”ERROR: error de open\n”); exit(0); } while (1) { bytes = read(fd, buf, BUF_SIZE); if (bytes <= 0) break; write(sa, buf, bytes); } close(fd); printf(”MENSAJE: archivo enviado correctamente.\n”); close(sa); } }
cliente.c
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/fcntl.h> #include <sys/unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define SERVER_PORT 7000 #define BUF_SIZE 4096 int main(int argc, char **argv) { int c, s, bytes, fd; char buf[BUF_SIZE]; struct hostent *h; struct sockaddr_in channel; if (argc !=3) { printf(”Uso: cliente <nombre_servidor> <nombre_archivo>\n”); exit(0); } // direccion del host h = gethostbyname(argv[1]); if (!h) { printf(”ERROR: fallo al intentar resolver el nombre del servidor.\n”); exit(0); } s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s < 0) { printf(”ERROR: error el tratar de abrir el socket.\n”); exit(0); } memset(&channel, 0, sizeof(channel)); channel.sin_family= AF_INET; memcpy(&channel.sin_addr.s_addr, h->h_addr, h->h_length); channel.sin_port = htons(SERVER_PORT); c = connect(s, (struct sockaddr *) &channel, sizeof(channel)); if (c < 0) { printf(”ERROR: error el tratar de abrir un conexión.\n”); exit(0); } /* Se establece la conexion, se envia el nombre del archivo incluyendo el byte 0 al final */ write(s, argv[2], strlen(argv[2])+1); /* obtiene el archivo y escribe en la salida estandar */ fd = open(argv[2], O_CREAT | O_WRONLY); while (1) { // lee el socket bytes = read(s, buf, BUF_SIZE); // verifica el final del archivo if ( bytes <= 0 ) break; write(fd, buf, bytes); } close(fd); }
Para compilar ambos códigos, es necesario tener el gcc funcionando en nuestra distribución de linux. Una vez compilado el código, ejecutamos el servidor en “background”:
gcc -o servidor servidor.c
./servidor &
Luego, para el cliente, hacemos lo mismo, y ejecutamos. En este caso, asumo que el servidor tiene el ip 192.168.1.100 y que en el mismo directorio donde se está ejecutando, existe un archivo llamado texto.txt. Una vez que ejecutamos, cambiamos el permiso del archivo para poder visualizarlo, y luego hacemos cat para ver el contenido.gcc -o cliente cliente.c
./cliente 192.168.1.100 prueba.txt chmod 777 texto.txt cat texto.txt
Pues bien, eso es todo. En teoría debe de funcionar con cualquier tipo de archivo (en mis pruebas, no tuve ningún problema).
Ojo: como se darán cuenta, el código es muy básico y hay muchas cosas que se pueden mejorar (poder definir el puerto utilizando parámetros, cambiar el open por el fopen, etc.) pero la idea principal es aprender a utilizar sockets y entender como funcionan. Ése es el objetivo principal.
Espero que a alguien le haya ayudado.