Cómo crear un chat con NodeJS
Crear un chat con NodeJS implica varias cosas, crear un servidor y también manejar sockets y con la ayuda de HTML5 y los WebSockets ya la cosa es supremamente fácil.
Pero existen librerías muy buenas, muy estables y muy famosas en NodeJS para el manejo de Sockets y esa es la que vamos a emplear.
La librería se llama
Socket.IO
Entonces los pasos para crear nuestro chat ULTRA SENCILLO, me conecto al servidor, me pongo un nick, y puedo hablar con la gente que está conectada.
OJO que no voy a pulir el código de presentación del chat, va a ser lo más sencillo posible y lo trataré de explicarlo lo mejor y más detalladamente posible y pues a la final lo que importa es el cómo se hace, el qué bonito se va a ver lo dejo en sus manos e imaginación.
1. Creamos la carpeta donde vamos a poner el servidor y sus cositas entonces digitamos el siguiente comando
Código:
mkdir chatLaneros
cd chatLaneros
2. Ahora instalamos la librería de Socket.IO que es la vamos a utilizar con NodeJS.
3. Creamos nuestro archivo server.js y le inyectamos el siguiente código
Código:
var io = require('socket.io');
var socket = io.listen(9876);
socket.on('connection', function(client) {
client.join('laneros');
client.on('message', function(event) {
console.log('Mensaje recibido del cliente! ', event);
client.send(event);
});
client.on('chat', function(data) {
client.broadcast.in('laneros').emit('LALOS', data);
client.in('laneros').emit('LALOS', data);
});
client.on('disconnect', function() {
console.log('El servidor fue desconectado');
});
});
Guardamos y ejecutamos de la siguiente manera
Pero bueno antes de continuar voy a explicar qué está haciendo nuestro servidor sockets para el chat que estamos haciendo
Código:
var io = require('socket.io');
Esto lo que hace es invocar la librería que hemos instalado e "instanciarla" en la variable "io"
Código:
var socket = io.listen(9876);
Resulta que la librería ya crea con esta línea un servidor web y lo pone a escuchar por el puerto indicado, en este caso sería por el puerto "9876" y lo instancia en la variable "socket".
Ahora si llega lo que se llama "Programación por eventos" ¿Qué sucede cuando el cliente se conecta?
Código:
socket.on('connection', function(client) {
Resulta que en esta línea estamos definiendo qué va a suceder cuando el cliente se conecte a nuestro servidor. Y lo que va a suceder es qué
Primero
El cliente se va a unir a un salón por así llamarlo, llamado "laneros"
Segundo
Vamos a definir todos los eventos que el cliente tendrá para "llamar" al servidor una ves esté conectado. Dos eventos que siempre van a estar presentes del lado del servidor son los siguientes "message" y "disconnect".
La estructura de un evento siempre es la siguiente:
Código:
client.on([NOMBRE_EVENTO], function([VARIABLE_CON_DATOS]) {
[CODIGO_QUE_HACER_CON_LOS_DATOS]
});
Entendiendo lo anterior ahora procedo a explicar la pequeña función central que hace funcionar a nuestro Chat.
Código:
client.on('chat', function(data) {
client.broadcast.in('laneros').emit('LALOS', data);
client.in('laneros').emit('LALOS', data);
});
La función anterior... o mejor el evento anterior se llama "chat" y lo que está haciendo es, emitir a todas las personas que están metidas en el salón "laneros" la variable "data" a evento "LALOS" que se encuentra en el cliente. (Si no comprendiste no te preocupes que cuando veamos el código del cliente todas nuestras dudas se aclararán)
La segunda línea lo que hace es emitir la variable "data" al usuario que emitió el mensaje original y también hacia el evento "LALOS" que se encuentra en el cliente. Esta parte hay que hacerla ya que la primera línea emite a todos menos a la persona que lo origina inicialmente y necesitamos que la persona que lo emite también le llegue el mensaje.
Y creo que con esto estamos listos para proseguir con el código del cliente
4. Como ya vamos a pasar el cliente, primero que todo debemos de copiar la librería para el cliente quien nos va a manejar los WebSockets si es que están disponibles, sino la librería intentará manejar la situación por AJAX y en caso extremo de no poderse entonces intentará utilizar FLASH (más adelante hablaré de las diversas técnicas que existen para simular el comportamiento de sockets desde el navegador)
En la carpeta donde vamos a poner el archivo .html que es el que va de cara al usuario, vamos a copiar todo el contenido de la carpeta "dist" que se encuentra dentro de la carpeta donde hemos puesto el archivo server.js
Sí todo va según lo planeado entonces a nivel de la consola debería esos archivos encontrarse en la siguiente dirección:
Código:
node_modules/socket.io/node_modules/socket.io-client/dist/
Ahí hay 4 archivos socket.io.js, socket.io.min.js, WebSocketMainInsecure.swf y WebSocketMain.swf
Los cuales vamos a copiar en la raíz de donde vamos a poner nuestro archivo chat.html que es el que va a consumir el cliente desde el navegador.
Ahora bien... el código de nuestro chat sería el siguiente:
Código:
<!DOCTYPE html>
<html>
<head>
<title>Chat Laneros</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="socket.io.min.js"></script>
<script>
var socket;
$(document).ready(function() {
socket = new io.connect('localhost', {
port: 9876
});
socket.on('connect', function() {
console.log('Client has connected to the server!');
});
socket.on('message', function(data) {
$('#chat').prepend('<div>' + data.usu + ': ' + data.msg + '</div>');
});
socket.on('LALOS', function(data) {
$('#chat').append('<div>' + data.usu + ': ' + data.msg + '</div>');
});
socket.on('disconnect', function() {
console.log('The client has disconnected!');
$('#chat').html('');
});
});
function sendMessageToServer() {
var message = $('#mensaje');
var usuario = $('#nick');
socket.emit('chat', {usu: usuario.val(), msg: message.val()});
message.val('').focus();
}
</script>
</head>
<body>
<div style="width: 300px">
<div id="chat" style="height: 100%"></div>
<div>
<input type="text" id="nick" />
<br />
<input type="text" id="mensaje" /><input type="button" id="btnEnviar" value="Enviar" onclick="sendMessageToServer()" />
</div>
</div>
</body>
</html>
Explico el código del cliente
Antes que nada estamos llamando la librería jQuery para un mejor manejo del HTML en forma dinámica, y luego estamos llamando la liberaría que hemos copiado de Socket.IO
Lo bueno de la librería Socket.IO es que mira por qué técnica soporta el navegador en el cual se está ejecutando técnica iFrames para navegadores realmente viejos, Long Poll (Ajax) para navegadores un poco más actuales pero que no soportan WebSockets, los mismos WebSockets para navegadores de última generación que soportan HTML5 y por si fuera poco ps si ni lo uno ni lo otro, entonces tienen un pequeño archivo .swf (archivo flash) para establecer la conexión con el servidor que hemos creado con NodeJS.
En el código del cliente lo que hacemos básicamente es
Primero: creamos una variable global llamada "socket".
Segundo: cuando el documento ya cargó y con ayuda de jQuery "$(document).ready()" entonces lo que hacemos es a la variable global instanciar la clase de Socket.IO de la siguiente manera
Código:
socket = new io.connect('localhost', {
port: 9876
});
Resulta que hay 4 eventos que siempre deberíamos escribir porque son eventos que responden a... eventos del servidor por así decirlo y para una mejor comprensión los voy a poner en una tabla de equivalencia.
Código:
+-----------------------------------------+
+ Servidor + Cliente +
+-----------------------------------------+
+ connection + connect +
+ message + message +
+ disconnect + disconnect +
+-----------------------------------------+
Pero si la única que se ve diferente es la de conexión en cliente y en servidor y las otras? entonces vamos explicar qué sucede para tener claro el movimiento de esto:
connection/connect: resulta que cuando alguien se conecta al servidor, se activa el evento
connection y cuando el cliente se ha conectado al servidor entonces se activa el evento
connect.
message/message: resulta que cuando en el servidor escribimos lo siguiente
client.send(mensaje); entonces quien recibe ese mensaje en el cliente es el evento
message y cuando desde el cliente escribimos
socket.send(mensaje); entonces quien recibe ese mensaje en el servidor es el evento
message ¿ahora si se comprende por qué ese evento es mejor ponerlo? porque ese tipo de mensajes es más bien utilizado como para decirle algo al servidor o accionar algo especial porque de resto cuando necesitamos accionar un evento desde el servidor hacía el cliente entonces útilizamos la siguiente manera
client.emit([NombreEvento], dataJSON); y cuando necesitamos accionar un evento desde el cliente al servidor, entonces lo escribimos de la siguiente manera
socket.emit([NombreEvento], datosJSON);
Para resumir, el metodo para accionar un evento es
emit.
disconnect/disconnect: resulta y acontece que cuando un cliente se desconecta, en el servidor se dispara inmediatamente el evento
disconnect y cuando el servidor por X o Y motivo se cae, se cierra o pasa algo que saca a todo el mundo, entonces en el cliente se activa también el evento
disconnect.
Y ya para finalizar explico la siguiente función Javascript que está en el cliente
Código:
function sendMessageToServer() {
var message = $('#mensaje');
var usuario = $('#nick');
socket.emit('chat', {usu: usuario.val(), msg: message.val()});
message.val('').focus();
}
Lo que se hace aquí es muy sencillo y resulta que estoy sacando el mensaje y el usuario en variables y luego estoy armando un JSON con dichos valores de la siguiente manera
Código:
{usu: usuario.val(), msg: message.val()}
Para después accionar el evento
chat desde el cliente en el servidor de la siguiente manera y pasando el JSON
Código:
socket.emit('chat', {usu: usuario.val(), msg: message.val()});
En el servidor quien me recibe lo anterior es el siguiente código
Código:
client.on('chat', function(data) {
client.broadcast.in('laneros').emit('LALOS', data);
client.in('laneros').emit('LALOS', data);
});
El cual ya creo que he explicado pero vuelvo a explicar para que no retrocedan
Primero hay que decirle a toda la gente que está conectada que ha llegado un nuevo dato ¿cómo hago esto?
Código:
client.broadcast.emit('LALOS', data);
Aquí entra el metodo
broadcast, pero resulta que solo se lo quiero decir a las personas que están en el salón "laneros" entonces para eso mejor empleamos la siguiente línea:
Código:
client.broadcast.in('laneros').emit('LALOS', data);
Ahora bien resulta que el metodo
broadcast solo a todo el muno MENOS a la persona quien ha originado el mensaje; entonces para poder también avisarle a la persona que originalmente ha generado dicho mensaje y que se encuentra en el canal "laneros", procedemos con la siguiente línea:
Código:
client.in('laneros').emit('LALOS', data);
Y listo chicos hasta aquí el chat funcionando de lo lindo, ya podemos empezar a crear chats más vistosos.
Para más información (y si que la hay) los invito a ver
la documentación de Socket.IO aquí