Al iniciar un servidor desde consola, Si cierras la terminal sin elegir una opción y el servidor está encendido, este continuará funcionando en segundo plano hasta que se interrumpa de alguna manera. El servidor permanece activo porque no hay una lógica que detenga el proceso automáticamente al cerrar la terminal. Esto significa que el socket y los recursos asociados al servidor no se liberarán correctamente.
En sistemas operativos basados en Unix (como Linux o macOS), puedes verificar y detener el servidor manualmente utilizando comandos como:
ps aux | grep nombre_del_programa: Este comando te permite identificar el proceso del servidor en ejecución.kill PID: Una vez que tengas el ID de proceso (PID) del servidor, puedes usar este comando para detenerlo.
Si prefieres evitar este problema, puedes implementar mecanismos para manejar la interrupción al cerrar la terminal, como señales (SIGHUP) o un gestor externo que supervise el estado del servidor y libere los recursos apropiadamente.
He agregado un menú interactivo simple que permite al usuario elegir si desea iniciar o detener el servidor.
- Manejo de señal (
SIGINT): Se implementa una funciónhandle_sigintque se ejecuta al recibir la señalSIGINT(por ejemplo, Ctrl + C en la consola) para cerrar el socket y detener el servidor de manera limpia. - Menú interactivo: Se agrega un menú que permite al usuario iniciar o detener el servidor. Esto se logra con un bucle
whiley opciones que el usuario selecciona. - Inicio del servidor: La función
start_servercontiene la lógica para crear, configurar y aceptar conexiones en el servidor. Este proceso se inicia al elegir la opción 1 del menú. - Detener el servidor: El usuario puede detener el servidor seleccionando la opción 2 del menú. También se garantiza que se cerrará el socket si el servidor es detenido manualmente o con
Ctrl + C.
Manejo de la señal SIGHUP
El manejo de la señal SIGHUP, que permite detectar cuando la terminal se cierra y liberar los recursos asociados al servidor correctamente.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int server_fd;
int running = 1;
// Manejo de señales para interrupción (SIGINT) y cierre de terminal (SIGHUP)
void handle_signal(int sig) {
if (sig == SIGINT) {
printf("\nServidor detenido por SIGINT (Ctrl + C).\n");
} else if (sig == SIGHUP) {
printf("\nServidor detenido por cierre de terminal (SIGHUP).\n");
}
if (server_fd) {
close(server_fd);
}
exit(0);
}
void start_server() {
int new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
const char *message = "Hello from the C server!\n";
// Crear socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket failed");
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 con dirección
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// Escuchar conexiones
if (listen(server_fd, 3) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Servidor en ejecución en el puerto %d\n", PORT);
printf("\n Nota: Presione (Ctrl + C) para deterner el Servidor \n");
// Aceptar conexiones
while (running && (new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) >= 0) {
read(new_socket, buffer, BUFFER_SIZE);
send(new_socket, message, strlen(message), 0);
close(new_socket);
}
}
int main() {
int choice;
// Configurar manejo de señales
signal(SIGINT, handle_signal);
signal(SIGHUP, handle_signal); // Capturar cierre de terminal
while (1) {
printf("\n--- Menú del servidor ---\n");
printf("1. Iniciar servidor\n");
printf("2. Detener servidor: \n");
printf("Seleccione una opción: ");
scanf("%d", &choice);
if (choice == 1) {
printf("Iniciando servidor...\n");
start_server();
} else if (choice == 2) {
printf("Servidor detenido manualmente.\n");
running = 0;
if (server_fd) {
close(server_fd);
}
break;
} else {
printf("Opción no válida. Intente nuevamente.\n");
}
}
return 0;
}
Explicación del funcionamiento:
- Manejo de señales (
SIGINTySIGHUP):- La función
handle_signaldetecta señales comoSIGINT(cuando se presiona Ctrl + C) ySIGHUP(cuando se cierra la terminal). - En ambos casos, se imprime un mensaje indicando la causa de la interrupción, se libera el socket y se detiene el proceso.
- La función
- Gestión de recursos:
- Se asegura que el socket asociado al servidor se cierre adecuadamente en cualquier escenario, ya sea manualmente, por interrupción o por cierre de terminal.
- Funcionalidad del menú:
- Se mantiene el menú interactivo, permitiendo iniciar y detener el servidor según la selección del usuario.
- Compatibilidad con cierre de terminal:
- Con el uso de la señal
SIGHUP, el servidor detecta automáticamente cuando la terminal se cierra, lo que garantiza que los recursos del servidor no queden bloqueados.
- Con el uso de la señal
SIGINT
SIGINT es una inteligencia derivada de señales y sistemas electrónicos.
Se basa en la inteligencia de comunicaciones (COMINT), la inteligencia electrónica (ELINT) y la inteligencia de señales de instrumentación extranjera (FISINT).
Proporciona información sobre las capacidades, acciones e intenciones de los adversarios extranjeros.
SIGHUP
SIGHUP es una señal que se envía a un proceso cuando su terminal controlador se cierra.
- Se le conoce como "signal hang up".
- Se envía automáticamente a los procesos que se ejecutan en un terminal cuando el usuario se desconecta.
- Se usa para detener y reiniciar un proceso.
Otras señales
- SIGINT es una señal de interrupción que se envía con Ctrl+C.
- SIGTERM es una señal de terminación que indica al proceso que finalice limpiamente.
- SIGQUIT es similar a SIGINT, pero se controla con una tecla diferente.
- SIGKILL es una señal que termina el proceso inmediatamente.
