Servidor web sencillo en C bajo Linux

julio 26 200917 comentarios

Guardado en : General, Software Libre

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
unsigned short port = 8000; //Número de puerto predeterminado del servidor
 
#define DEBUG 1
 
char * error_return = "<HTML>\n<BODY>Archivo no encontrado\n</BODY>\n</HTML>";
char ret_buf[32768];
char * read_file(char * buf, int num_buf){
	int i;
	char *cp, *cp2;
	FILE *f;
	cp = buf + 5;
	cp2 = strstr(cp, " HTTP");
	if(cp2 != NULL) *cp2 = '\0';
	if(DEBUG) printf("file: |%s|\n",cp);
	//fetch file:
	f = fopen(cp, "r");
	if(f == NULL) return error_return;
	i = fread(ret_buf,1,32768,f);
	if(i == 0){fclose(f); return error_return;}
	ret_buf[i] = '\0';
	fclose(f);
	return ret_buf;
}
 
int main(int argc, char* argv[]){
	int i, sock;
	char* recvBuffer = (char *)malloc(4001);
	int rc = 0;
	int serversocket;
	struct sockaddr_in serverAddr;
	struct sockaddr_in clientAddr;
	int clientAddrSize;
	struct hostent* entity;
	int totalReceived;
	int size;
	int totalSent;
	int bytesSent;
	char * cbuf;
	serversocket = socket(AF_INET, SOCK_STREAM, 0);
	if(serversocket == -1){
		printf("Socket inválido\n");
		exit(1);
	}
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(port);
	serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	memset(&(serverAddr.sin_zero),0,8);
	printf("Unión del socket de servidor con el puerto %d\n",port);
	rc = bind(serversocket, (struct sockaddr*) &serverAddr, sizeof(struct 
sockaddr));
	if(rc == -1){
		printf("Bad bind\n");
		exit(1);
	}
	rc = listen(serversocket, 10);//permite 10 peticiones en cola
	if(rc == -1){
		printf("Bad listen\n");
		exit(1);
	}
	printf("Aceptando conexiones ...\n");
	while(1){
		clientAddrSize = sizeof(struct sockaddr_in);
		do
			sock = accept(serversocket,(struct sockaddr*) &clientAddr,
					&clientAddrSize);
		while((sock == -1) && (errno == EINTR));
		if(sock == -1){
			printf("Mala Aceptación\n");
			exit(1);
		}
		entity = gethostbyaddr((char *) &clientAddr.sin_addr,sizeof(struct 
in_addr),
					AF_INET);
		if(DEBUG) printf("Conexión desde %s\n",inet_ntoa((struct in_addr)clientAddr.sin_addr));
		i = recv(sock, recvBuffer,4000,0);
		if(i == -1) break;
		if(recvBuffer[i-1] != '\n') break;
		recvBuffer[i] = '\0';
		if(DEBUG){
			printf("Recibido del cliente: %s\n",recvBuffer);
		}
		//llama a una función de trabajo separada para procesar la petición:
		cbuf = read_file(recvBuffer, totalReceived);
		size = strlen(cbuf);
		totalSent = 0;
		do{
			bytesSent = send(sock,cbuf + totalSent,strlen(cbuf + totalSent),0);
			if(bytesSent == -1) break;
			totalSent += bytesSent;
		}while(totalSent < size);
		if(DEBUG) printf("Conexión cerrada por el cliente.\n");
		close(sock);
	}
	return 0;
}

Quizá te interese :

Acerca del autor:

17 Respuestas a “Servidor web sencillo en C bajo Linux”

  1. pitogrillo dice:

    Debe ser una confusión, pues ésto es un cliente, jeje

  2. Daniel dice:

    Corregido, (como se agregarón los dos seguidos cliente y servidor me equivoqué), gracias por tu comentario!

  3. Daniel dice:

    Amigo disculpa probé tu código y corre de maravilla, pasa archivos , paginas html, todo excepto imágenes. Que tendría que modificarle para que envié imágenes?

  4. Daniel dice:

    Darle soporte de MIME-Types e indicar en los encabezados el tipo de archivo que se va a descargar.

  5. Mario dice:

    Y en que parte del código exactamente hay que modificar para que acepte el MIME-Types de imagenes desde un html…

  6. Angel Fe dice:

    Hola!! También probé tu código y funciona perfectamente pero soy novato en esto de la programación en c y quería ver si es posible que me ayudaras con esa parte de que el cliente pueda ver imgenes desde un navegador…

  7. Daniel dice:

    Amigo disculpa que es lo que hace la linea 4 del metodo read_file, la que dice cp = buf + 5; por que le sumas 5?

  8. Creo que es por que le añade espacio-HTTP para la petición.

  9. Daniel dice:

    Amigo disculpa he estado trabajando con tu código, pero no tengo una duda. Resulta que cuando yo paso paginas html las envía bien, pero si envió imágenes dentro del html no las muestra, y algo curioso es que cuando envía archivos como por ejemplo .rar me los envía y el browser lo que hace es que lo ejecuta como descarga pero cuando finaliza tu te vas a la carpeta donde se encuentra esa descarga y esta el archivo pero no se pasa completo, tan solo envía 5 bytes nada mas, cualquier archivo que uno pase y sea mayor a 5 bytes solo se pasa 5 bytes. Me parece que podría ser que el buffer se llena, pero no se como ampliarlo. No se si me puedas ayudar con eso o dar una luz de por donde podría estar el problema. Gracias de antemano por toda tu ayuda anterior.

  10. Daniel dice:

    Vamos por partes Dan, para que sea un poco más fácil.
    a) Documentate un poco más del protocolo HTTP (aquí Protocolo HTTP), para tener una idea un poco más claro de que espera el webbrowser del webserver y podamos implementar el protocolo lo mejor posible.

    b) Utiliza Firebug y Firefox y analiza las peticiones que envías, y comparalas con las que un servidor completo (por ejemplo Apache) realiza, de esa manera lo que tengas leído del protocolo será mucho más claro y real.

    c) El fread que se usa para leer archivos, solo lee un bloque fijo, lo que tienes que hacer es asignar memoria dinámica y pasar el contenido del archivo, completo o en bloques (en este ejemplo lee 32768 bytes del archivo).

    d) Este servidor es muy sencillo, para darle el soporte de envío de múltiples archivos (como en apache), tendrías que añadir un fork o threads para que se hagan descargas simultáneas (este juguete es secuencial…)

    Espero te sirva y si pudieras publicar en el sitio los cambios sería algo súper.

    Saludos!

  11. sebas dice:

    hola…este codigo si no me equivoco es el mismo que el de programando en linux de kurt wall..estoy haciendo un web server y me estoy apoyando en este codigo. como hago para que el fread pueda continuar leyendo mientras recibe..porque el servidor tiene que ser capaz de desplegar fotos y musica…gracias por la ayuda..

  12. Daniel dice:

    Hola Sebas,
    Para que el fread sea capaz de ir leyendo tiene que usar memoria dinámica e ir leyendo ya sea bloques completos del archivo o el archivo mismo e ir enviandolo.
    Aparte para que sea “simúltanea” la descarga requieres hacer un fork (o usar threads) para la lectura/envio de los bloques y funcione normalmente.
    Si tienes dudas, puedes poner código aquí en el sitio o un link a pastebin, para que te podamos auxiliar :)
    Saludos.

  13. Larguirucho dice:

    Hola Daniel,
    Tengo que hacer un servidor web y me apoyo en este codigo, soy muy novato y el caso es que cuando ejecuto, se queda esperando en aceptando conexiones.
    En el mio, me gustaria poner el puerto que quisiera, esto con un scanf supongo que ya estaria.
    Por otra parte el programa tendria que ser concurrente, esto supongo que haciendo fork se podria arreglar.
    Me puedes introducir un poco por favor.
    Muchas gracias

  14. Daniel dice:

    Hola Larguirucho,
    En el caso de cambiar dinámicamente un puerto, como dices lo puedes leer con scanf o directamente usando la línea de comandos (args, argv).
    Sobre el fork la parte que debes de meter en ese bloque es la de la lectura (no leer por línea lee por bloque) y envío de archivos.
    Creo que es una solicitud muy frecuente y valdría la pena implementarla para complementar a este ejemplo.

  15. Larguirucho dice:

    Hola de nuevo, ya he implementado el fork, pero no lo puedo provar del todo porque me quedo atascado en el principio, le paso por parametro el puerto i el directorio, pero que directorio le tengo que pasar?
    Esto no lo acabo de entender.
    La estructura principal, de abrir sockets i todo eesto es facil, siempre lo mismo, o sea trivial pero me faltan nociones basicas de funcionalidad.
    Cuando vaya todo bien lo pegare por aqui.
    Muchas gracias de nuevo

  16. Daniel dice:

    Hola Largirucho,
    No pasa como parámetro el socket abierto, y puedes ser un tanto práctico, al momento de elegir que archivos pasar, (por ejemplo solo el directorio actual).
    El dejar la conexión compartida y que los procesos hagan el manejo será lo más sencillo.

  17. Señor C dice:

    Hola Daniel,
    Me podrias explicar que hace la función read_file()??

Deja un comentario


Licencia y uso

Las técnicas demostradas en los tutoriales pueden ser utilizadas sin ninguna limitación y tampoco es obligatorio dar una atribución.


Los textos, imágenes y tutoriales son propiedad de sus respectivos autores, nuestro contenido se encuentra bajo licencia Creative Commons Share-Alike.

Escribe algo para el sitio

El escribir un tutorial o un artículo, mandarnos un enlace para Ubicuos, no solamente es una forma de obtener publicidad, si no también de dar algo a la comunidad y nosotros te lo recompensamos con los premios del mes! Leer más de nuestras promociones

¿Sugerencias?

Este es TU sitio, si tienes sugerencias o ideas de cómo podemos mejorarlo para ti, ¡Por favor háznoslos saber!

Hacemos nuestro mayor esfuerzo en proporcionar un sitio útil y amigable y esperamos que disfrutes tu tiempo aquí.

Ayuda a Difundir

Te gusta Ubicuos?

Ve las formas en que nos puedes apoyar.

Apoyando a Ubicuos.com

Submit your linkClose

-->