Simples servidor http com concorrência feito em C
Publicado por Robson Lopes (última atualização em 05/08/2011)
[ Hits: 13.870 ]
Um simples servidor http com concorrência(fork ou thread) com objetivo de aumentar conhecimento na area de redes.
Há um makefile que possa ajudar na compilação do código.
Para rodar utilize o seguinte parametros:
./http <porta> -[fork ou thread]
Abra qualquer browser e teste. http://ipservidor:porta
Abraços
/* * Autor: Robson Oliveira * Obs.: Livre para qualquer alteração */ #include <stdio.h> /* printf */ #include <stdlib.h> /* exit */ #include <string.h> /* bzero */ #include <sys/socket.h> /* struct sockaddr, socket, listen, bind, accept, recv, send */ #include <sys/wait.h> /* waitpid */ #include <arpa/inet.h> /* struct sockaddr */ #include <unistd.h> /* exit, fork */ #include <signal.h> /* signal */ #include <time.h> /* TIME, time_t */ #include <pthread.h> /* pthread_t, pthread_create */ #include <sys/stat.h> /* lstat() */ #include <sys/types.h> /* mode_t */ /*define variaveis constantes e seus respectivos valores */ #define BUFFSIZE 800 #define MAXPENDING 5 #define SA struct sockaddr #define SAI struct sockaddr_in #define TYPE 16 #define tipoData "%a, %m %b %Y %X %Z" #define SERVER "AS(2008-2011)" void error(char *msg); /* imprime mensagens de erro */ void sig_chld(int sinal); /* trata o sinal, para evitar filhos zumbis */ char *extensao(char *nome); /* verifica e retorna a extensao do arquivo para respota HTTP*/ int redirect(char *caminho); /* Verifica se o arquivo não sofreu uma modificação permanente*/ void respostaHTTP(char *resposta, char *tipo, char* caminho, int connfd, char *size); /* Cria uma resposta Http e envia ao cliente*/ void enviaArquivo(char *caminho, int connfd); /* Envia o arquivo solicitado ao Cliente */ long verificaArquivo(char *caminho); /* Verifica se o arquivo solicitado existe na raiz */ /* Verifica o que é pedido no protocolo Http solicitado pelo cliente*/ int TratandoPedido(char *metodo, char *versao, char *caminho, char *p, char *tipo, int i); int pedidoHTTP(char *p, char *caminho, char *tipo);/* Manipula pedido do cliente essa função necessita da TratandoPedido */ int *criarSocket(int porta); /* Cria um socket utilizando TCP/IP */ void dec_string(long size, char *s); /* Transforma um inteiro em uma String*/ void execucao(int connfd); /* execucao da conexao com o metodo fork */ void in_fork(int *listenfd); /* metodo que utiliza o fork para execucao */ static void *execucao_thread(void *arg); /* execucao da conexao com o metodo thread */ void in_thread(int *listenfd); /* metodo que utiliza threads para execucao */ /* Função Principal*/ int main(int argc, char *argv[]){ int *listenfd, porta, concorrencia; /* Caso o usuario nao coloque os 3 parametros necessarios */ if(argc != 3) error("Use: HttpServer <porta> -[thread ou fork]"); /* define o modelo de concorrencia */ if(strcmp(argv[2], "-thread") == 0) concorrencia = 1; else if(strcmp(argv[2], "-fork") == 0) concorrencia = 2; else error("Por favor, escolha a concorrência sendo ela -fork ou -thread ."); porta = atoi(argv[1]); /*define a porta */ listenfd = criarSocket(porta); /* chama a funcao criarSocket */ /* Estipula a fila para o Servidor */ if(listen(*listenfd, MAXPENDING) < 0) error("Falha ao tentar escutar o socket do servidor"); if(concorrencia == 2) /* se concorrencia for fork */ signal(SIGCHLD, sig_chld); /* vai tratar os filhos zumbis */ if(concorrencia == 1){ /* se concorencia for thread */ in_thread(listenfd); /* chama o metodo in_thread */ } else if(concorrencia == 2){ /* se concorrencia for fork */ in_fork(listenfd); /* chama o metodo in_fork */ } free(listenfd); return 0; } /* Imprime mensagens de erro */ void error(char *msg){ printf("%s\n", msg); exit(0); return; } /* verifica e retorna a extensao do arquivo */ char *extensao(char *nome){ char ext[20]; strcpy(ext, "."); strcat(ext, nome); if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) return "text/html"; if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return "image/jpeg"; if (strcmp(ext, ".gif") == 0) return "image/gif"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".au") == 0) return "audio/basic"; if (strcmp(ext, ".wav") == 0) return "audio/wav"; if (strcmp(ext, ".avi") == 0) return "video/x-msvideo"; if (strcmp(ext, ".mpeg") == 0 || strcmp(ext, ".mpg") == 0) return "video/mpeg"; if (strcmp(ext, ".mp3") == 0) return "audio/mpeg"; if (strcmp(ext, ".js") == 0) return "text/javascript"; if (strcmp(ext, ".ico") == 0) return "image/x-icon"; return NULL; } void respostaHTTP(char *resposta, char *tipo, char *caminho, int connfd, char *size){ time_t rawtime; struct tm *timeinfo, *ltime; struct stat arq; char timebuf[50], encaminhar[BUFFSIZE], *aux, lastime[50]; long s; lstat(caminho, &arq); time(&rawtime); timeinfo = localtime(&rawtime); ltime = localtime(&arq.st_mtime); strftime(lastime, sizeof(lastime), tipoData, ltime); strftime(timebuf, sizeof(timebuf), tipoData, timeinfo); if(strcmp(resposta, "HTTP/1.1 200 OK") == 0){ strcpy(encaminhar, resposta); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Connection: close\r\n"); strcat(encaminhar, "Date: "); strcat(encaminhar, timebuf); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Server: "); strcat(encaminhar, SERVER); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Length: "); strcat(encaminhar, size); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Last-Modified: "); strcat(encaminhar, lastime); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Type: "); if(strcmp(tipo, "/") == 0){ aux = extensao("html"); strcat(encaminhar, aux); } else{ aux = extensao(tipo); if(aux != NULL) strcat(encaminhar, aux); else{ s= verificaArquivo("badrequest.html"); dec_string(s, size); respostaHTTP("HTTP/1.1 400 Bad Request", tipo, "badrequest.html", connfd, size); enviaArquivo("badrequest.html", connfd); close(connfd); exit(0); } } strcat(encaminhar, "\r\n"); strcat(encaminhar, "\r\n"); printf("%s\n", encaminhar); send(connfd, encaminhar, strlen(encaminhar), 0); } else if(strcmp(resposta, "HTTP/1.1 404 Not Found") == 0){ strcpy(encaminhar, resposta); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Connection: close\r\n"); strcat(encaminhar, "Date: "); strcat(encaminhar, timebuf); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Server: "); strcat(encaminhar, SERVER); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Length: "); strcat(encaminhar, size); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Last-Modified: "); strcat(encaminhar, lastime); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Type: "); aux = extensao("html"); strcat(encaminhar, aux); strcat(encaminhar, "\r\n"); strcat(encaminhar, "\r\n"); printf("%s\n", encaminhar); } else if(strcmp(resposta, "HTTP/1.1 505 HTTP Version Not Supported") == 0){ strcpy(encaminhar, resposta); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Connection: close\r\n"); strcat(encaminhar, "Date: "); strcat(encaminhar, timebuf); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Server: "); strcat(encaminhar, SERVER); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Length: "); strcat(encaminhar, size); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Last-Modified: "); strcat(encaminhar, lastime); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Type: "); aux = extensao("html"); strcat(encaminhar, aux); strcat(encaminhar, "\r\n"); strcat(encaminhar, "\r\n"); printf("%s\n", encaminhar); } else if(strcmp(resposta, "HTTP/1.1 400 Bad Request") == 0){ strcpy(encaminhar, resposta); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Connection: close\r\n"); strcat(encaminhar, "Date: "); strcat(encaminhar, timebuf); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Server: "); strcat(encaminhar, SERVER); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Length: "); strcat(encaminhar, size); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Last-Modified: "); strcat(encaminhar, lastime); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Type: "); aux = extensao("html"); strcat(encaminhar, aux); strcat(encaminhar, "\r\n"); strcat(encaminhar, "\r\n"); printf("%s\n", encaminhar); } else if(strcmp(resposta, "HTTP/1.1 301 Moved Permanently") == 0){ strcpy(encaminhar, resposta); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Connection: close\r\n"); strcat(encaminhar, "Date: "); strcat(encaminhar, timebuf); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Server: "); strcat(encaminhar, SERVER); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Length: "); strcat(encaminhar, size); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Location: "); strcat(encaminhar, caminho); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Last-Modified: "); strcat(encaminhar, lastime); strcat(encaminhar, "\r\n"); strcat(encaminhar, "Content-Type: "); aux = extensao("html"); strcat(encaminhar, aux); strcat(encaminhar, "\r\n"); strcat(encaminhar, "\r\n"); printf("%s\n", encaminhar); } return; } /* Envia o arquivo solicitado ao cliente */ void enviaArquivo(char *caminho, int connfd){ FILE *file; long size; char *c; file = fopen(caminho, "rb"); if(file){ fseek(file , 0 , SEEK_END); size = ftell(file); rewind(file); c = (char*) malloc(sizeof(char) * size); fread(c, 1, size, file); send(connfd, c, size, 0); fclose(file); free(c); } return; } long verificaArquivo(char *caminho){ FILE *file; long size; file = fopen(caminho, "rb"); if(file){ fseek(file , 0 , SEEK_END); size = ftell(file); rewind(file); fclose(file); return size; } else return 0; } int TratandoPedido(char *metodo, char *versao, char *caminho, char *p, char *tipo, int i){ char *HTTP="HTTP/1.1"; int k; for(i = i+1, k = 0; p[i] != ' ' && p[i] != '{FONTE}'; i++, k++) caminho[k] = p[i]; caminho[k] = '{FONTE}'; for(i = i+1, k = 0; p[i] != '{FONTE}' && k < 8; i++, k++) versao[k] = p[i]; versao[k] = '{FONTE}'; if((strcmp(HTTP, versao) != 0) && (strcmp("HTTP/1.0", versao) != 0)) return 0; if(strcmp(caminho, "/") != 0){ for(i = strlen(caminho); p[i] != '.' && i >=0; i--); for(i = i+1, k = 0; p[i] != '{FONTE}' && p[i] != ' '; i++, k++) tipo[k] = p[i]; tipo[k] = '{FONTE}'; } else strcpy(tipo, "/"); return 1; } int pedidoHTTP(char *p, char *caminho, char *tipo){ char *GET="GET", *POST="POST", metodo[9], versao[10]; char aux; int i, k, l; for(i = 0; p[i] != ' ' && p[i] != '{FONTE}' && i < 9; i++) metodo[i] = p[i]; metodo[i] = '{FONTE}'; if(strcmp(GET, metodo) == 0){ if(!TratandoPedido(metodo, versao, caminho, p, tipo, i)) return 2; } else if(strcmp(POST, metodo) == 0){ i = strlen(p) - 1; for(k = 0; p[i] != '\n'; k++, i--) tipo[k] = p[i]; tipo[k] = '{FONTE}'; l = strlen(tipo) - 1; for(k = 0, i = l; k <= l/2; k++, i--){ aux = tipo[k]; tipo[k] = tipo[i]; tipo[i] = aux; } return 3; } else{ return 0; } return 1; } /* Cria um socket TCP/IP */ int *criarSocket(int porta){ int *listenfd; struct sockaddr_in servaddr; /* Define um socket para o servidor */ listenfd = (int *) malloc(sizeof(int)); /* malloc define um ponteiro para um espaco de memoria do tamanho solicidado */ /* chama a funcao socket para especificar o tipo do protocolo */ if((*listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) error("Falha ao criar o socket"); /*populando os dados do servidor*/ bzero(&servaddr, sizeof(servaddr)); /*zera a estrutura que armazenarah os dados do servidor */ servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Aceita qualquer faixa de IP que a maquina possa responder. */ servaddr.sin_port = htons(porta); /* define a porta */ /* vincula um socket a um endereco */ if(bind(*listenfd, (SA *) &servaddr, sizeof(servaddr)) < 0) error("Falha ao observar o socket do servidor"); return listenfd; } int redirect(char *caminho){ FILE *file; char novo[BUFFSIZE], antigo[BUFFSIZE]; file = fopen("htaccess.serv", "rb"); if(file){ while(!feof(file)){ fscanf(file, "%s %s", antigo, novo); if(strcmp(antigo, caminho) == 0){ strcpy(caminho, novo); return 1; } } return 0; } else return 0; } void dec_string(long size, char *s){ int i, j, l; char aux; for(i = 0; size > 0; size = size/10, i++){ s[i] = '0' + ((size%10) - 0); } s[i] = '{FONTE}'; j = strlen(s) - 1; for(l = 0; l < i/2; l++, j--){ aux = s[l]; s[l] = s[j]; s[j] = aux; } return; } void execucao(int connfd){ char buffer[BUFFSIZE], caminho[BUFFSIZE], tipo[BUFFSIZE], sizechar[TYPE]; char POST[BUFFSIZE]; int situacao, n, i, novo; long size; /*Rebendo Protocolo http do cliente*/ if((n = recv(connfd, buffer, BUFFSIZE, 0)) < 0) error("Falhou ao receber os dados iniciais do cliente"); buffer[n] = '{FONTE}'; printf("%s\n", buffer); printf("-------------------- Resposta Servidor -------------------\n"); situacao = pedidoHTTP(buffer, caminho, tipo); if(situacao == 1){ /*Verica se o link foi mudado, veja o arquivo htaccess.serv.*/ novo = redirect(caminho); if(strcmp(caminho, "/") == 0) strcpy(caminho, "index.html"); else{ for(i = strlen(caminho); i>=0; i--) caminho[i+1] = caminho[i]; caminho[0] = '.'; } size = verificaArquivo(caminho); if(size){ dec_string(size, sizechar); if(!novo){ respostaHTTP("HTTP/1.1 200 OK", tipo, caminho, connfd, sizechar); enviaArquivo(caminho, connfd); } else{ respostaHTTP("HTTP/1.1 301 Moved Permanently", tipo, caminho, connfd, sizechar); enviaArquivo(caminho, connfd); } } else{ size = verificaArquivo("notfound.html"); dec_string(size, sizechar); respostaHTTP("HTTP/1.1 404 Not Found", "html", caminho, connfd, sizechar); enviaArquivo("notfound.html", connfd); } } else if(situacao == 2){ size = verificaArquivo("notsupported.html"); dec_string(size, sizechar); respostaHTTP("HTTP/1.1 505 HTTP Version Not Supported", "html", caminho, connfd, sizechar); enviaArquivo("notsupported.html", connfd); } else if(situacao == 3){ size = sizeof(tipo); dec_string(size, sizechar); respostaHTTP("HTTP/1.1 200 OK", "html", caminho, connfd, sizechar); strcpy(POST, "<hmtl>\n<head>\n<title>Post</title>\n</head>\n<body>"); strcat(POST, "\n<b>Post:</b> "); strcat(POST, tipo); strcat(POST, "\n</body>\n</html>"); send(connfd, POST, sizeof(POST), 0); } else{ size = verificaArquivo("badrequest.html"); dec_string(size, sizechar); respostaHTTP("HTTP/1.1 400 Bad Request", "html", caminho, connfd, sizechar); enviaArquivo("badrequest.html", connfd); } printf("--------------------- Fim Comunicação --------------------\n\n"); return; } /* Trata o sinal enviado pelo sistema */ void sig_chld(int sinal){ pid_t pid; int stat; while((pid = waitpid(-1, &stat, WNOHANG)) > 0); return; } void in_fork(int *listenfd){ struct sockaddr_in client; int connfd; socklen_t clientlen; pid_t pid; for( ; ; ){ clientlen = sizeof(client); /* aceita a conexao com o cliente */ if((connfd = accept(*listenfd, (SA *) &client, &clientlen)) < 0) error("Falhou ao aceitar a conexao do cliente"); printf("------------------ Pedido de: %s ------------------\n", inet_ntoa(client.sin_addr)); /* imprime IP do cliente */ if((pid = fork()) == 0){ close(*listenfd); execucao(connfd); close(connfd); /* fecha a conexao */ exit(0); } close(connfd); /*fecha a conexao*/ } close(*listenfd); /*fecha a escuta*/ exit(0); return; } /* Função de execução da Thread */ static void *execucao_thread(void *arg){ int connfd; connfd = *((int *) arg); pthread_detach(pthread_self()); execucao(connfd); close(connfd); return NULL; } /* metodo que utiliza threads para execucao */ void in_thread(int *listenfd){ struct sockaddr_in client; /* define um socket para o cliente */ socklen_t clientlen; int *iptr; pthread_t tid; for( ; ; ){ iptr = (int *) malloc(sizeof(int)); /* aloca iptr para cada thread */ *iptr = accept(*listenfd, (SA *) &client, &clientlen); /* iptr aceita a escuta do cliente */ printf("------------------ Pedido de: %s ------------------\n", inet_ntoa(client.sin_addr)); /* imprime o endereco IP do cliente */ /* cria uma thread */ pthread_create(&tid, NULL, &execucao_thread, iptr); } return; }
Passkeys: A Evolução da Autenticação Digital
Instalação de distro Linux em computadores, netbooks, etc, em rede com o Clonezilla
Título: Descobrindo o IP externo da VPN no Linux
Armazenando a senha de sua carteira Bitcoin de forma segura no Linux
Enviar mensagem ao usuário trabalhando com as opções do php.ini
Instalando Brave Browser no Linux Mint 22
vídeo pra quem quer saber como funciona Proteção de Memória:
Encontre seus arquivos facilmente com o Drill
Mouse Logitech MX Ergo Advanced Wireless Trackball no Linux
Compartilhamento de Rede com samba em modo Público/Anônimo de forma simples, rápido e fácil
Remoção de propaganda com o programa Comskip[AJUDA] (4)
Instalação do drive do adaptador wiffi (5)
Linux Lite Demorando Muito Para Ligar (1)