Sumérgete en el núcleo de la comunicación en tiempo real Este proyecto te desafía a construir un servidor IRC funcional desde cero, utilizando C++ y conceptos clave de redes. Descubre cómo gestionar múltiples usuarios, canales dinámicos y mensajes en tiempo real, ¡todo mientras dominas un protocolo histórico de Internet!
Aquí tienes algunas imágenes del funcionamiento del server IRC:
¡Estas imágenes muestran lo que puedes lograr con el proyecto y te dan una idea de cómo se verán los resultados finales!
Un servidor IRC (Internet Relay Chat) es un sistema que permite la comunicación en tiempo real entre múltiples usuarios a través de canales de texto. Este proyecto te permite:
- Gestionar Conexiones Simultáneas: Maneja docenas de usuarios conectados al mismo tiempo sin bloqueos.
- Crear Canales Dinámicos: Los usuarios pueden unirse, crear y gestionar canales de chat públicos o privados.
- Implementar Comandos Básicos y Avanzados: Desde autenticación (
PASS
,NICK
,USER
) hasta gestión de canales (MODE
,KICK
,INVITE
).
Continúa leyendo para descubrir cómo configurar y utilizar este servidor, incluyendo cómo gestionar usuarios, canales y mensajes en tiempo real.
Para tu proyecto de servidor IRC, el índice debe reflejar los aspectos clave del desarrollo y funcionamiento del servidor, así como los conceptos técnicos y las funcionalidades que implementaste. Aquí tienes una propuesta de índice adaptado a tu proyecto:
- Introducción
- Arquitectura del Servidor
- Comandos Básicos y de Operadores
- Estructuras de Datos
- Manejo de Mensajes y Errores
- Compilación y Ejecución
- Demostración
El servidor IRC está diseñado para manejar múltiples conexiones de clientes de manera eficiente. Para lograrlo, utiliza mecanismos como poll()
, fcntl()
y estructuras como sockaddr_in
y pollfd
. Estas herramientas permiten que el servidor gestione múltiples clientes sin necesidad de crear un hilo por cada conexión, lo que optimiza el rendimiento y evita bloqueos innecesarios.
Cuando un servidor necesita escuchar conexiones entrantes, debe especificar en qué dirección y puerto lo hará. Aquí es donde entra sockaddr_in
, una estructura fundamental para configurar el socket.
Ejemplo de uso:
struct sockaddr_in socketAddress;
socketAddress.sin_family = AF_INET; // IPv4
socketAddress.sin_port = htons(this->_port); // Puerto en formato de red
socketAddress.sin_addr.s_addr = INADDR_ANY; // Escucha en todas las interfaces
¿Por qué se usa? Permite al servidor definir su punto de escucha en la red, asegurando que pueda recibir conexiones de los clientes.
El servidor IRC debe gestionar varios clientes al mismo tiempo sin bloquearse. pollfd
permite hacerlo al monitorear múltiples descriptores de archivo.
Ejemplo de uso:
struct pollfd newPoll;
newPoll.fd = _mainSocket; // Descriptor de archivo a monitorear
newPoll.events = POLLIN; // Detecta datos disponibles para leer
newPoll.revents = 0; // Se llena automáticamente con los eventos ocurridos
¿Por qué se usa? Permite al servidor reaccionar solo cuando hay actividad en los sockets, evitando ciclos innecesarios y mejorando la eficiencia.
En el bucle principal del servidor, poll()
es clave para detectar actividad en los sockets de manera eficiente.
Ejemplo de uso:
poll(&_pollSocketFds[0], _pollSocketFds.size(), -1);
Opciones clave:
POLLIN
: Detecta si hay datos disponibles para leer.POLLOUT
: Detecta si el socket está listo para escribir.
¿Por qué se usa? Permite manejar múltiples conexiones en un solo hilo, evitando la sobrecarga de procesos adicionales.
Si el servidor esperara datos en un socket de manera bloqueante, se detendría hasta que llegara algo, lo que no es viable en un entorno con múltiples clientes.
Ejemplo de uso:
fcntl(_mainSocketFd, F_SETFL, O_NONBLOCK);
Opción clave:
O_NONBLOCK
: Permite que el socket funcione sin bloquear la ejecución del programa.
¿Por qué se usa? Evita que el servidor se quede atascado esperando datos, permitiendo que siga gestionando otros clientes sin interrupciones.
El servidor IRC sigue un proceso estructurado para manejar conexiones y datos de manera eficiente.
- Se crea un socket con
socket()
. - Se configura para reutilizar direcciones (
SO_REUSEADDR
) y operar sin bloqueos (O_NONBLOCK
). - Se enlaza a una dirección y puerto con
bind()
. - Se pone en modo de escucha con
listen()
. - Se añade el socket principal a
pollfd
para monitorear nuevas conexiones.
- Se ejecuta
poll()
para detectar actividad en los sockets. - Si hay actividad en el socket principal, se acepta una nueva conexión con
accept()
. - Si hay actividad en un cliente, se leen sus datos con
recv()
.
accept()
recibe la conexión y devuelve un nuevo descriptor de archivo para el cliente.- Se configura en modo no bloqueante y se añade a
pollfd
.
- Se leen los datos con
recv()
. - Si el cliente se desconecta (
recv()
devuelve 0), se eliminan sus recursos. - Si hay datos, se procesan y ejecutan los comandos apropiados.
Gracias a esta arquitectura, el servidor IRC puede manejar múltiples conexiones de manera eficiente, evitando bloqueos y optimizando el uso de los recursos del sistema.
Este servidor IRC soporta una variedad de comandos para la autenticación, gestión de usuarios y canales, y privilegios especiales para operadores.
PASS <contraseña>
: Autentica al usuario con la contraseña del servidor.NICK <apodo>
: Asigna un apodo (nickname) al usuario.USER <username> <hostname> <servername> <realname>
: Registra al usuario en el servidor.
JOIN <canal>
: Une al usuario a un canal.PART <canal>
: Abandona un canal.PRIVMSG <destinatario> <mensaje>
: Envía un mensaje a un canal o usuario.
KICK <canal> <usuario>
: Expulsa a un usuario de un canal.INVITE <usuario> <canal>
: Invita a un usuario a un canal.TOPIC <canal> <tema>
: Cambia el tema de un canal.MODE <canal> <modo>
: Configura modos como contraseñas, límites de usuarios o permisos.
📖 Más detalles: README: Comandos Basicos
El servidor IRC utiliza tres estructuras principales para gestionar usuarios, canales y conexiones:
- Propósito: Gestiona el estado global del servidor.
- Propósito: Representa un canal de chat.
- Propósito: Representa un usuario conectado.
📖 Más detalles: README: Estructura del servidor IRC
Para que la comunicación en el servidor IRC sea clara y eficiente, es fundamental contar con un sistema robusto de envío de respuestas y manejo de errores. Esto no solo garantiza que los usuarios reciban retroalimentación inmediata, sino que también protege la estabilidad del servidor ante situaciones inesperadas.
El servidor envía mensajes a los clientes a través de la función SendResponse
, ubicada en src/functions/SendResponse
. Esta función es clave para asegurar una comunicación fluida.
¿Cómo funciona?
- Antes de enviar datos, el servidor verifica si el socket del cliente está listo para escribir usando
poll()
. - Esto evita bloqueos y asegura que el servidor pueda seguir procesando otras conexiones sin interrupciones.
Ejemplo de uso:
struct pollfd pfd;
pfd.fd = clientFd;
pfd.events = POLLOUT;
poll(&pfd, 1, TIMEOUT);
if (pfd.revents & POLLOUT)
send(clientFd, message.c_str(), message.length(), 0);
¿Por qué se usa?
- Evita bloqueos: No se queda esperando indefinidamente a que el socket esté listo.
- Mejora el rendimiento: Permite que el servidor gestione múltiples clientes simultáneamente.
Para mantener un formato estandarizado en las respuestas, el servidor utiliza macros definidas en includes/comunication/
. Estas macros facilitan la generación de mensajes según el protocolo IRC.
Mensajes que indican acciones exitosas:
RPL_CONNECTED
: Confirma la conexión del usuario al servidor.RPL_JOINMSG
: Notifica a los usuarios cuando alguien se une a un canal.RPL_TOPICIS
: Informa sobre el tema actual de un canal.
Los errores informan a los usuarios sobre problemas o restricciones:
ERR_NICKINUSE
: El apodo seleccionado ya está en uso.ERR_NOTOPERATOR
: Se intenta ejecutar un comando de operador sin los permisos necesarios.ERR_INCORPASS
: La contraseña ingresada es incorrecta.
Ejemplo de error en código:
SendResponse(ERR_NICKINUSE("Bob"), clientFd); // Informa que el apodo 'Bob' está en uso
¿Por qué se usa?
- Código más claro: Las macros hacen que el código sea más fácil de leer y mantener.
- Estandarización: Todos los mensajes siguen el protocolo IRC, asegurando compatibilidad.
El sistema de manejo de mensajes y errores del servidor IRC sigue este diseño por varias razones:
Claridad: Las macros predefinidas permiten escribir código más limpio y fácil de entender.
Eficiencia: poll()
optimiza el envío de datos evitando bloqueos innecesarios.
Robustez: Un buen manejo de errores previene fallos inesperados y mejora la experiencia del usuario.
Sigue estos pasos para poner en marcha el servidor IRC:
- Abre una terminal en el directorio del proyecto.
- Ejecuta:
Esto compilará el código y generará el ejecutable
make
ircserv
.
-
Usa el siguiente comando para iniciar el servidor:
./ircserv <puerto> <contraseña>
<puerto>
: Puerto en el que el servidor escuchará (por ejemplo,6667
).<contraseña>
: Contraseña que los clientes deben usar para conectarse.
Ejemplo:
./ircserv 6667 mySecurePassword
- Abre un cliente IRC como HexChat o Irssi.
- Conéctate al servidor usando la dirección
localhost
y el puerto especificado. - Autentícate con la contraseña proporcionada.
Para ilustrar cómo funciona el proyecto en la práctica, hemos preparado una serie de videos que muestran diferentes aspectos y funcionalidades del programa en acción. Estos videos cubren:
Estos clips te proporcionarán una visión clara de cómo utilizar el programa y lo que puedes esperar en términos de rendimiento y visualización.
Si deseas contribuir al proyecto:
- Fork el repositorio.
- Crea una rama para tus cambios.
- Realiza los cambios necesarios.
- Envía un Pull Request describiendo los cambios realizados.
◦ Email Oseivane: oseivane@student.42barcelona.com
◦ Email gmacias-: gmacias-@student.42barcelona.com