Criando um Servidor HTTP Simples em C

Anderson L Pereira
4 min readJan 5, 2025

--

Desenvolver um servidor HTTP do zero pode parecer uma tarefa intimidante, mas com a linguagem C, é possível criar um servidor funcional com algumas linhas de código. Neste artigo, vamos explorar um exemplo de implementação de um servidor HTTP básico que responde às solicitações com uma mensagem simples: “Hello, World!”. Além disso, explicaremos linha a linha, incluindo o uso de bibliotecas, ponteiros e movimentação de memória.

Objetivo do Tutorial

Nosso objetivo é entender:

  • Como configurar sockets em C.
  • Os passos para criar um servidor HTTP que responda às solicitações.
  • Detalhar cada linha do código, bibliotecas utilizadas e conceitos de memória.

O Código

Abaixo está o código completo do servidor HTTP em C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
const char *response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 13\r\n"
"\r\n"
"Hello, World!";

// Criando o socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Erro ao criar o socket");
exit(EXIT_FAILURE);
}

// Configurando o endereço do servidor
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// Ligando o socket ao endereço e porta
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Erro ao associar o socket");
close(server_fd);
exit(EXIT_FAILURE);
}

// Colocando o socket em modo de escuta
if (listen(server_fd, 3) < 0) {
perror("Erro ao escutar");
close(server_fd);
exit(EXIT_FAILURE);
}

printf("Servidor HTTP iniciado na porta %d...\n", PORT);

// Loop para aceitar conexões
while (1) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) {
perror("Erro ao aceitar conexão");
continue;
}

// Lendo a solicitação do cliente (opcional, dependendo do uso)
read(new_socket, buffer, BUFFER_SIZE);
printf("Requisição recebida:\n%s\n", buffer);

// Enviando a resposta HTTP
write(new_socket, response, strlen(response));

// Fechando o socket do cliente
close(new_socket);
}

// Fechando o socket do servidor
close(server_fd);

return 0;
}

Explicando o Código

1. Incluindo Bibliotecas Necessárias

  • <stdio.h>: Fornece funções para entrada e saída, como printf e perror.
  • <stdlib.h>: Usada para alocação de memória dinâmica (como malloc e free) e para controle de saída com exit.
  • <string.h>: Inclui funções de manipulação de strings, como strlen e memset.
  • <unistd.h>: Necessária para funções como read, write e close.
  • <arpa/inet.h>: Fornece funções e estruturas para comunicação de sockets baseados em redes.

2. Definindo Constantes

  • #define PORT 8080: Define a porta na qual o servidor irá escutar.
  • #define BUFFER_SIZE 1024: Define o tamanho do buffer para leitura das requisições.

3. Criando o Socket

A função socket(AF_INET, SOCK_STREAM, 0) cria um socket:

  • AF_INET: Especifica o uso do protocolo IPv4.
  • SOCK_STREAM: Define o uso do protocolo TCP.
  • O valor de retorno é um descritor de arquivo que representa o socket. Se retornar 0, houve um erro.

4. Configurando o Endereço do Servidor

  • sin_family: Define a família do protocolo como IPv4.
  • sin_addr.s_addr: Configura o endereço IP. INADDR_ANY permite que o servidor escute em todos os IPs disponíveis.
  • sin_port: Define a porta, convertida para o formato de rede com htons.

5. Ligando o Socket ao Endereço

  • A função bind associa o socket ao endereço e porta especificados. Caso falhe, close é usado para liberar recursos.

6. Escutando por Conexões

  • listen(server_fd, 3): Coloca o socket em modo passivo, permitindo que ele aceite conexões. O "3" define a capacidade da fila de conexões pendentes.

7. Aceitando Conexões

  • accept: Bloqueia o programa até que uma conexão seja aceita. Retorna um novo descritor para o socket conectado.

8. Movimentação de Memória

  • O buffer é inicializado na pilha com char buffer[BUFFER_SIZE] = {0};. Isso evita lixo de memória.
  • O ponteiro para o endereço é passado como struct sockaddr *, garantindo que a função accept utilize a memória alocada adequadamente.

9. Enviando Respostas

  • write(new_socket, response, strlen(response)): Escreve diretamente no socket do cliente. Não há alocação dinâmica aqui.

10. Fechando Conexões

  • Cada conexão é encerrada com close(new_socket) para liberar os recursos do socket.

Considerações Sobre Ponteiros e Memória

  • Ponteiros: Usados para referência ao endereço na função bind e accept.
  • Memória: O buffer e variáveis locais são armazenados na pilha, sem uso de malloc ou free. A movimentação de memória ocorre nas transferências de dados via socket.

Como Executar o Servidor

1-) Compile o código:

gcc -o http_server http_server.c

2-) Execute o servidor:

./http_server

3-) Acesse o servidor no navegador ou com curl:

curl http://localhost:8080

Você deve ver a mensagem “Hello, World!” no navegador ou no terminal.

Conclusão

Este exemplo demonstra como é possível criar um servidor HTTP básico em C. Apesar de simples, é um ponto de partida para aprender sobre redes, sockets e conceitos de memória. Em projetos mais complexos, considere o uso de bibliotecas especializadas ou frameworks.

Se você gostou deste tutorial, deixe um comentário ou compartilhe com outros entusiastas da programação!

--

--

No responses yet