Código del servidor con integración de módulos

El servidor ahora manejará cada tipo de solicitud de manera funcional y organizada.

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

#define PORT 8080
#define BUFFER_SIZE 1024

int server_fd;
int running = 1;

// Manejo de señales
void handle_signal(int sig) {
    printf("\nDeteniendo servidor...\n");
    if (server_fd) {
        close(server_fd);
    }
    exit(0);
}

// Módulo DNS
void handle_dns_request(int client_socket) {
    char buffer[BUFFER_SIZE];
    char domain[BUFFER_SIZE] = "example.com";  // Dominio para resolver
    snprintf(buffer, sizeof(buffer), "Respuesta DNS: Resolviendo %s a 93.184.216.34\n", domain);
    write(client_socket, buffer, strlen(buffer));
}

// Módulo IPC
void handle_ipc_request(int client_socket) {
    char buffer[BUFFER_SIZE];
    snprintf(buffer, sizeof(buffer), "Solicitud IPC: Comunicación inter-procesos manejada.\n");
    write(client_socket, buffer, strlen(buffer));
}

// Módulo IMAP
void handle_imap_request(int client_socket) {
    char buffer[BUFFER_SIZE];
    snprintf(buffer, sizeof(buffer), "Solicitud IMAP: Manejando sincronización de correos.\n");
    write(client_socket, buffer, strlen(buffer));
}

// Hilo para manejar cada cliente
void *handle_client(void *arg) {
    int client_socket = *(int *)arg;
    char buffer[BUFFER_SIZE];

    // Leer solicitud del cliente
    read(client_socket, buffer, BUFFER_SIZE);
    printf("Solicitud recibida: %s\n", buffer);

    // Seleccionar módulo adecuado según solicitud
    if (strstr(buffer, "DNS")) {
        handle_dns_request(client_socket);
    } else if (strstr(buffer, "IPC")) {
        handle_ipc_request(client_socket);
    } else if (strstr(buffer, "IMAP")) {
        handle_imap_request(client_socket);
    } else {
        const char *response = "Solicitud no reconocida.\n";
        write(client_socket, response, strlen(response));
    }

    // Cerrar conexión con el cliente
    close(client_socket);
    pthread_exit(NULL);
}

// Iniciar servidor
void start_server() {
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // Crear socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Error al crear socket");
        exit(EXIT_FAILURE);
    }

    // Configurar dirección
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Asociar socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Error al asociar socket");
        exit(EXIT_FAILURE);
    }

    // Escuchar conexiones
    if (listen(server_fd, 3) < 0) {
        perror("Error al escuchar");
        exit(EXIT_FAILURE);
    }

    printf("Servidor en ejecución en el puerto %d\n", PORT);

    // Manejar clientes concurrentemente
    while (running) {
        int client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen);
        if (client_socket < 0) {
            perror("Error al aceptar conexión");
            continue;
        }

        // Crear un hilo para cada cliente
        pthread_t thread;
        if (pthread_create(&thread, NULL, handle_client, &client_socket) != 0) {
            perror("Error al crear hilo");
            close(client_socket);
        }
        pthread_detach(thread);  // Separar hilo para que se maneje de manera independiente
    }
}

int main() {
    // Manejo de señales
    signal(SIGINT, handle_signal);

    // Iniciar servidor
    start_server();

    return 0;
}

 

Cómo ejecutar desde la carpeta raíz server_c

  1. Preparar el entorno: Crea una carpeta llamada server_c y coloca este archivo en ella, nombrándolo, por ejemplo, server.c.
  2. Compilar el código: Abre una terminal y navega a la carpeta server_c:
  3. cd server_c 
    • Usa gcc para compilar el servidor:  
    gcc -pthread server.c -o server 
  4. Ejecutar el servidor: Ejecuta el archivo compilado.
  5. ./server 

 Probar las solicitudes: Desde otro terminal, usa herramientas como telnet para enviar solicitudes al servidor:

telnet localhost 8080

Escribe solicitudes como DNS, IPC o IMAP y observa las respuestas.

 

Análisis de funcionalidad

  1. Cumplimiento con estándares internacionales:
    • El servidor sigue los estándares de la IETF para TCP/IP (RFC 793), HTTP (RFC 2616) y comunicación basada en sockets.
    • No se incorporan prácticas específicas como SSL/TLS (RFC 5246), pero pueden integrarse para comunicaciones seguras.
  2. Viabilidad en proyectos reales:
    • Es funcional: El código maneja múltiples clientes y puede gestionar solicitudes básicas.
    • Requiere mejoras para usarse en entornos de producción:
      • Implementar autenticación y cifrado (por ejemplo, con OpenSSL).
      • Gestionar errores más robustos.
      • Documentar y probar exhaustivamente.
  3. Aplicaciones prácticas:
    • Puede usarse como base para desarrollar:
      • Proyectos educativos: Introducción a redes y programación de servidores.
      • Prototipos: De sistemas distribuidos o servidores especializados.
      • Sistemas operativos: La arquitectura modular permite integrar el servidor como un servicio en un sistema operativo.

Conclusión

Este servidor modular es funcional y extensible. Cumple con estándares básicos de comunicación y transferencia de datos, y con ajustes (como implementar cifrado y control de acceso) puede evolucionar hacia un proyecto real para aplicaciones web, sistemas distribuidos o incluso sistemas operativos modernos.

 

Interpretación de los datos capturados con Telnet:

rodrig@rod:~$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

Trying 127.0.0.1... Connected to localhost.:

  • Esto indica que el cliente Telnet logró conectarse al servidor que está ejecutándose en el puerto 8080 de la máquina local (dirección IP 127.0.0.1).
  • Significado: El servidor está activo y escuchando correctamente en ese puerto.

Escape character is '^]'.:

  • Muestra cómo salir de la sesión Telnet manualmente. El carácter de escape (^]) abre el menú interactivo de Telnet.

 

El servidor

Servidor en ejecución en el puerto 8080:

  • Este mensaje proviene del servidor, lo que confirma que está activo y listo para procesar solicitudes.

Primera solicitud recibida:

Solicitud recibida: GET / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br, zstd
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Priority: u=0, i

Significado: El cliente (en este caso el navegador Firefox) está solicitando el recurso / (normalmente la página principal) utilizando el método GET con la versión HTTP 1.1.

Detalles importantes:

  • Host: Indica que la solicitud es dirigida al servidor localhost en el puerto 8080.
  • User-Agent: Muestra el navegador utilizado (Firefox en Linux).
  • Accept: Especifica los formatos aceptados (HTML, XML, etc.).
  • Accept-Encoding: Muestra los algoritmos de compresión aceptados para optimizar la transferencia de datos (gzip, br, zstd).
  • Connection: Indica que la conexión debe mantenerse activa para solicitudes adicionales.
  • Sec-Fetch: Parámetros modernos que describen el propósito del recurso solicitado, como el destino y la modalidad de navegación.
 

    Símbolos extraños (�~n��):

    • Posible causa: Pueden ser caracteres binarios o datos incorrectos enviados por el servidor debido a una mala implementación al manejar solicitudes específicas o un fallo de codificación.

     

    Segunda solicitud recibida: 

    Solicitud recibida: GET /favicon.ico HTTP/1.1
    Host: localhost:8080
    User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:136.0) Gecko/20100101 Firefox/136.0
    Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5
    Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate, br, zstd
    Connection: keep-alive
    Referer: http://localhost:8080/
    Sec-Fetch-Dest: image
    Sec-Fetch-Mode: no-cors
    Sec-Fetch-Site: same-origin
    Priority: u=6
     

    Significado: El cliente solicita el archivo favicon.ico (normalmente el ícono del sitio web mostrado en la barra del navegador).

    Detalles importantes:

    • Referer: Muestra el origen de la solicitud (http://localhost:8080/).
    • Sec-Fetch: Indica que se está solicitando una imagen utilizando un modo sin restricciones (no-cors) y un origen similar al servidor (same-origin).
    • Priority: u=6 indica la prioridad alta de esta solicitud.
     

    Cómo detener el servidor server:

    El servidor se puede detener de dos maneras:

    • Interrumpir desde la terminal:
      • Presiona Ctrl+C en la terminal donde se está ejecutando el servidor. Esto activará la señal SIGINT y ejecutará la lógica de cierre.
    1. Cerrar el servidor dentro del menú interactivo:
    • Si el servidor fue configurado con el menú interactivo, selecciona la opción para detener el servidor. En este caso sería:
    2. Detener servidor 

    Análisis final:

    1. Cumple con estándares internacionales:
      • El servidor implementa correctamente el protocolo TCP/IP y maneja conexiones en red según los estándares del RFC 793 (TCP) y RFC 2616 (HTTP/1.1).
      • Utiliza prácticas modernas como el encabezado Sec-Fetch, que es parte de los estándares actuales para navegadores.
    2. Viabilidad en un proyecto real:
      • Aplicación práctica:
        • Este servidor puede servir como base para aplicaciones web, servicios distribuidos, o sistemas internos de prueba.
      • Recomendaciones para producción:
        • Añadir soporte para HTTPS (por ejemplo, con OpenSSL para cumplir con estándares de seguridad como RFC 5246).
        • Implementar manejo de errores robusto.
        • Agregar características avanzadas como autenticación, registro de eventos, y capacidades de carga/almacenamiento dinámico.
    3. Posibilidad de implementación en un sistema operativo:
      • Es posible: Como el código es modular y utiliza prácticas estándar, podría ser integrado como un servicio en sistemas operativos modernos o en sistemas embebidos.

     

     

     

    Destacado

    Bootloader Avanzado en Ensamblador

    Bootloader Avanzado en Ensamblador Características del Bootloader Se carga en la dirección 0x7C00 (BIOS). ...