Sistema cliente-servidor implementado en C que permite ejecutar comandos Unix remotamente mediante sockets TCP/IP.
- Jorge Salgado Miranda
- Joshua Ivan Lopez Nava
Proyecto académico para el curso de Arquitectura Cliente-Servidor que implementa un sistema similar a SSH. El cliente se conecta al servidor a través de sockets TCP, envía comandos que son ejecutados en el servidor, y recibe la salida completa (stdout + stderr) de vuelta.
- Comunicación mediante sockets TCP/IP (AF_INET, SOCK_STREAM)
- Ejecución remota de comandos Unix con
popen() - Protocolo de comunicación con longitud prefijada para transmisión confiable
- Validación de comandos con lista de comandos prohibidos
- Manejo robusto de errores y recursos
- Soporte para múltiples comandos con sus opciones
- Desconexión limpia con comandos
saliroexit - Compatible con Linux y MacOS
- Código 100% en C (estándar C99)
- Sistema operativo: Linux o MacOS
- Compilador: GCC 4.8+ o Clang con soporte para C99
- Librerías estándar de C (POSIX)
El proyecto consta de tres archivos fuente principales:
src/cliente.c- Implementación del clientesrc/servidor.c- Implementación del servidorsrc/common.h- Definiciones compartidas y funciones del protocolo
gcc -Wall -Wextra -std=c99 -o servidor src/servidor.cgcc -Wall -Wextra -std=c99 -o cliente src/cliente.cgcc -Wall -Wextra -std=c99 -o servidor src/servidor.c
gcc -Wall -Wextra -std=c99 -o cliente src/cliente.cEl servidor debe iniciarse primero y requiere especificar el puerto como argumento:
./servidor <puerto>Ejemplo:
./servidor 8080El servidor quedará escuchando conexiones en el puerto especificado.
El cliente requiere la dirección IP (o hostname) del servidor y el puerto:
./cliente <IP_o_hostname> <puerto>Ejemplos:
Conexión local:
./cliente localhost 8080Conexión remota:
./cliente 192.168.1.100 8080Una vez conectado, el cliente mostrará el prompt comando> donde puedes escribir comandos Unix:
comando> pwd
/Users/usuario/proyecto
comando> ls -la
total 64
drwxr-xr-x 10 usuario staff 320 Nov 17 14:00 .
drwxr-xr-x 20 usuario staff 640 Nov 17 13:00 ..
-rw-r--r-- 1 usuario staff 4499 Nov 17 14:00 README.md
drwxr-xr-x 5 usuario staff 160 Nov 17 12:00 src
...
comando> date
Sun Nov 17 14:30:25 CST 2025
comando> whoami
usuario
comando> salir
Cerrando conexión...El servidor acepta la mayoría de comandos Unix estándar, incluyendo:
pwd- Directorio actualls,ls -la,ls -lh- Listar archivosdate- Fecha y hora del sistemawhoami- Usuario actualhostname- Nombre del hostps,ps aux- Lista de procesoscat <archivo>- Mostrar contenido de archivoecho <texto>- Imprimir textouname -a- Información del sistema- Y cualquier otro comando Unix no interactivo
Por limitaciones técnicas, los siguientes comandos están prohibidos:
cd- No cambia el directorio de trabajo del servidortop,htop- Comandos con salida dinámica no soportadosvim,nano,less,more- Editores interactivos no soportadosssh- Conexiones SSH anidadas no permitidas
Si intentas ejecutar un comando prohibido, recibirás un mensaje de error:
comando> cd /tmp
ERROR: Comando 'cd' está prohibidoPara cerrar la conexión con el servidor:
comando> salirO también:
comando> exitEl servidor continuará ejecutándose y podrá aceptar nuevas conexiones.
El proyecto utiliza un protocolo simple pero robusto de longitud prefijada:
-
Envío de datos:
- Se envían 4 bytes con la longitud del mensaje (en network byte order)
- Luego se envían los datos del mensaje
-
Recepción de datos:
- Se leen primero los 4 bytes de longitud
- Se reserva memoria dinámica para el mensaje
- Se leen los datos según la longitud recibida
Este protocolo asegura que los mensajes se transmitan completos, manejando correctamente el caso donde send() o recv() no transmiten todos los bytes en una sola llamada.
Cliente Servidor
│ │
│──────── 1. Conectar (TCP) ──────────────► │
│ │
│──── 2. Enviar comando (ej: "ls -la") ───► │
│ │
│ 3. Validar
│ comando
│ │
│ 4. Ejecutar
│ con popen()
│ │
│◄──── 5. Retornar salida (stdout+stderr) ─ │
│ │
│──────── 6. Mostrar resultado ──────────── │
│ │
│──────── 7. Nuevo comando o salir ──────── │
.
├── src/
│ ├── cliente.c # Implementación del cliente TCP
│ ├── servidor.c # Implementación del servidor TCP
│ └── common.h # Protocolo de comunicación compartido
├── README.md # Este archivo
└── .gitignore # Archivos excluidos de git
El sistema incluye manejo robusto de errores:
- Validación de argumentos de línea de comandos
- Verificación de errores en llamadas de sistema (socket, bind, listen, accept, connect)
- Validación de comandos antes de ejecutarlos
- Detección de errores en la ejecución de comandos
- Liberación correcta de recursos (memoria, sockets, descriptores de archivo)
- Mensajes de error descriptivos en español
Terminal 1 (Servidor):
$ ./servidor 8080
Servidor iniciado en puerto 8080
Esperando conexiones...
Cliente conectado desde 127.0.0.1:54321
Comando recibido: pwd
Comando ejecutado exitosamente
Comando recibido: ls -la
Comando ejecutado exitosamente
Cliente desconectado
Esperando conexiones...Terminal 2 (Cliente):
$ ./cliente localhost 8080
Conectado al servidor localhost:8080
Escribe 'salir' o 'exit' para terminar
comando> pwd
/Users/usuario/proyecto
comando> ls -la
total 64
drwxr-xr-x 10 usuario staff 320 Nov 17 14:00 .
drwxr-xr-x 20 usuario staff 640 Nov 17 13:00 ..
-rw-r--r-- 1 usuario staff 4499 Nov 17 14:00 README.md
-rwxr-xr-x 1 usuario staff 34632 Nov 17 14:00 cliente
-rwxr-xr-x 1 usuario staff 51944 Nov 17 14:00 servidor
drwxr-xr-x 5 usuario staff 160 Nov 17 12:00 src
comando> salir
Cerrando conexión...
Desconectado del servidor- Compilar ambos programas
- En Terminal 1:
./servidor 8080 - En Terminal 2:
./cliente localhost 8080 - Ejecutar varios comandos
- Verificar que la salida es correcta
- Compilar en dos máquinas diferentes en la misma red
- En Máquina A (servidor):
./servidor 8080 - Obtener IP de Máquina A:
ifconfig | grep inet - En Máquina B (cliente):
./cliente <IP_de_A> 8080 - Ejecutar comandos y verificar que se ejecutan en Máquina A
- Intentar comando prohibido:
cd /tmp→ Debe mostrar ERROR - Intentar comando inexistente:
comando_falso→ Debe mostrar error del sistema - Probar comando con archivo:
cat README.md→ Debe mostrar contenido
- El servidor maneja un cliente a la vez (implementación secuencial)
- No soporta comandos interactivos que requieren entrada del usuario
- No soporta comandos que cambian el estado del shell (como
cd) - No soporta comandos con salida dinámica continua (como
top) - El tamaño máximo del comando es de 1024 bytes
- El tamaño máximo de la salida es de 64KB
- El servidor usa
popen()con redirección2>&1para capturar tanto stdout como stderr - Los mensajes de error del servidor se muestran en rojo en terminales que soportan colores ANSI
- El cliente valida la respuesta del servidor antes de mostrarla
- El protocolo usa
htonl()yntohl()para portabilidad entre arquitecturas - Todos los buffers tienen protección contra desbordamiento
Proyecto académico para el curso de Arquitectura Cliente-Servidor. Universidad Nacional Autónoma de México (UNAM) - Facultad de Ingeniería.