Categorías
HowTo

Servidor de Archivos / Sockets en C

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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.