Simples servidor http com concorrência feito em C
Publicado por Robson Lopes (última atualização em 05/08/2011)
[ Hits: 14.162 ]
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;
}
Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)
Linux em 2025: Segurança prática para o usuário
Desktop Linux em alta: novos apps, distros e privacidade marcam o sábado
IA chega ao desktop e impulsiona produtividade no mundo Linux
Novos apps de produtividade, avanços em IA e distros em ebulição agitam o universo Linux
Como instalar o repositório do DBeaver no Ubuntu
Como instalar o Plex Media Server no Ubuntu
Digitando underscore com "shift" + "barra de espaços"
Como ativar a lixeira e recuperar aquivos deletados em um servidor Linux
Como mudar o nome de dispositivos Bluetooth via linha de comando
O programa assinador digital (1)
PIP3 - erro ao instalar módulo do mariadb para o Python (9)
É normal não gostar de KDE? (8)
dpkg: erro: gatilho de arquivo duplicado chamado pelo arquivo de nome (6)









