Pre-requisitos
Para poder realizar esta guía debes tener instalado Node.js y npm previamente (luego realizare un post sobre esto). Además de tener un explorador con el cual podamos soportar javascript y html5.
Introducción
La idea de este post es tratar de explicar como se puede realizar un Chat simple utilizando Node.js, HTML, Javascript y Web Sockets. Se debe aclarar que esto es totalmente académico y por motivos de explicar las posibilidades que brinda Node.js para realizar aplicaciones en tiempo real.
Quiero darle gracias especiales a Julian Duque (@julian_duque) por la conferencia en MedellinJS (@medellinjs) del uso de Web Sockets de la forma correcta. Aquí pueden ver la conferencia original de Julian Duque con la que me base para realizar este post.
Que es WebSocket?
El significado que yo interpreto es "WebSocket es un protocolo de comunicación que nos permite enviar y recibir información hacia y desde el servidor en un solo canal". Básicamente nos da la posibilidad de mantener una comunicación con el servidor y que este nos avise de algún cambio que haya ocurrido en nuestro back-end. Está herramienta nos permite realizar paginas mucho mas eficientes en el consumo de trafico de red, ya que antiguamente para estar consultando que estaba pasando en nuestro back-end debíamos realizar llamados periódicos a nuestro servidor usando Ajax y cuando encontráramos un cambio presentarlo en pantalla.
Por tanto el uso de WebSockets para la creación de paginas web de real time es una gran herramienta y abre el espectro para aplicaciones que tengan que estar revisando cambios en la Base de datos o en sistemas de archivo.
Node.js , WebSockets, Socket.io y más..
En este momento para nadie debe ser un secreto que Node.js esta tomando mucha fuerza y esta permitiendo realizar aplicaciones realmente asombrosas. En este caso voy a explicar que posibilidades tenemos para usar WebSockets en nuestras aplicaciones Node.js.
WebSockets
En Node.js tenemos una librería que podemos usar para WebSocket que se llama "ws" (https://npmjs.org/package/ws) el cual dicen en su página de npm es posiblemente la mas rápida de todas las librerías realizadas para Node.js en el uso de WebSockets.
Ejemplo
Servidor
var WebSocketServer = require('ws').Server
, wss = new WebSocketServer({port: 8080});
wss.on('connection', function(ws) {
ws.on('message', function(message) {
console.log('received: %s', message);
});
ws.send('something');
});
var WebSocket = require('ws');
var ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function() {
ws.send('something');
});
ws.on('message', function(data, flags) {
// flags.binary will be set if a binary data is received
// flags.masked will be set if the data was masked
});
Socket.io
Aunque ws es probablemente el mecanismo mas eficiente para el uso de WebSockets, tiene limitaciones en cuanto el uso de los puertos, el envío de variables y el manejo de eventos personalizados. Por tanto hay librerías mucho mas completas y con herramientas que nos facilitan muchas operaciones. Es aquí donde entra Socket.io (https://npmjs.org/package/socket.io), que es probablemente la librería para el uso de WebSockets mas popular en Node.js.
Ejemplo
Servidor
var socketio = require('socket.io'); var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); fs.createReadStream('./index.html').pipe(res); }); server.listen(80); var io = socketio.listen(server); io.on('connection', function (socket) { socket.emit('Event', 'from server!'); socket.on('Event', function (msg) { console.log('Received: %s', msg); socket.emit('world'); }); });
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('Event', function (data) {
console.log(data);
socket.emit('otro evento', { my: 'data' });
});
</script>
Engine.io
Todo indica que Socket.io es lo mejor para usar WebSockets en Node.js, pero se han presentado problemas en aplicaciones con mas de 100 usuarios concurrentes, es por esto que la comunidad ha intentado crear soluciones a estos problemas de concurrencia para la librería de Socket.io, por esto surge Engine.io.
Servidor
Cliente
var engine = require('engine.io') , server = engine.listen(80) server.on('connection', function (socket) { socket.send('utf 8 string'); });
var engine = require('engine.io')
, http = require('http').createServer().listen(3000)
, server = engine.attach(http)
server.on('connection', function (socket) {
socket.on('message', function () { });
socket.on('close', function () { });
});
Manos a la obra
Lo primero que vamos hacer es instalar las dependencias que necesitamos para el chat, en este caso vamos a utilizar Socket.io. Entonces empecemos, creamos una carpeta y descargamos con npm las dependencias, para esto ingresamos a la consola de comandos y ejecutamos lo siguiente:
mkdir Chat cd Chat npm install socket.ioUna vez ejecutemos estos comandos vamos a tener un ambiente listo para desarrollar nuestra aplicación con socket.io en Node.js. Ahora nos dedicamos a crear el servidor del node.js que va manejar nuestro chat, para esto creamos un archivo llamado "chat.js" dentro de nuestra carpeta Chat con el siguiente código:
Como pueden ver estamos creando las variables socketio, http y fs. La variable de socketio nos va permitir usar la librería socketio, la variable http nos va permitir iniciar un servidor http donde vamos a exponer nuestra pagina web y la variable fs nos permite leer archivos del sistema. Para esto iniciamos nuestro servidor el cual va soportar html y leemos un archivo html "index.html" el cual vamos a usar como nuestra vista. Finalmente lo que le decimos a nuestro servidor es que se va ejecutar en el puerto 8080. Este es el código del archivo html que debemos guardar al mismo nivel del archivo "chat.js":var socketio = require('socket.io'); var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); fs.createReadStream('./index.html').pipe(res); }); server.listen(8080);
Con esto ya podemos ver nuestra aplicación corriendo con tan solo correr el siguiente comando:<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>WebSockets Chat Example</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"> </script> <script src="/socket.io/socket.io.js"></script> <style> .leftSide{ float:left; min-width:25%; min-height:400px; margin:10px; padding:15px; border:1px solid #333;} .rightSide{ float:right; min-width:65%;min-height:400px; margin:10px; padding:15px;border:1px solid #333; } </style> </head> <body> <h2>Welcome to the WebSockets Chat</h2> <section class="user"> <label>User Name </label> <input type="text" id="user"/> <input type="button" value="Join" id="btnJoin"/> </section> <section id="chat"> <section class="leftSide"></section> <section class="rightSide"></section> <section> <input type="button" value="Send" id="send" style="margin:10px; padding:10px;" /> <input type="text" id="textSend" style="margin:10px;"/> </section> </section> </body> </html>
node chat.js
Como ven ya tenemos nuestra pagina web, solicitando un usuario para iniciar el chat, por el momento nuestra pagina no va hacer nada, para agregar la funcionalidad de nuestro chat vamos agregar el siguiente código al archivo "chat.js":
Ahora pasamos al lado del cliente donde vamos a consumir todos los eventos que acabamos de agregar a nuestro chat, además de algunos eventos para iniciar el chat y ocultar algunos controles en la vista. Para esto agregamos un elemento script en nuestro archivo "index.html" con el siguiente código:var users = [];//Con esto manejamos el listado de nuestros usuarios var io = socketio.listen(server);//Iniciamos la libreria con nuestro servidor//Controlamos el evento cuando se conecte e imprimimos en consola el mensaje recibido//emitiendo el evento hello hacia el cliente con un mensaje inicial. io.on('connection', function (socket) { socket.on('hello', function (msg) { console.log('Received: %s', msg); socket.emit('hello', 'Hello from server'); }); socket.on('login', function (msg) { console.log('Received Login: %s', msg); socket.emit('login', {"user": "System","text": "Welcome to the chat"}); users.push(msg); });//Por defecto enviamos un mensaje de bienvenida para el evento messagesocket.emit('message', '<p class="message">Welcome from server!</p>');//Controlamos el evento message y replicamos el mensaje a todos los clientes//que se encuentren conectados socket.on('message', function (msg) { console.log('Received message: %s', msg); var text = '<p class="message"><b>'+msg.user+'</b>:'+msg.message+'</p>'; io.sockets.emit('message', {"user": msg.user,"text": text}); });//Controlamos el evento list que nos va servir para ver el listado de usuarios//conectados socket.on('list', function (msg) { console.log('Received list: %s', msg); socket.emit('list', users); }); });
Con esto ya tenemos la logica del servidor y la logica del cliente para ejecutar nuestro chat. Por lo que ejecutamos el comando://Inicializamos nuestras variablesvar socket = null; var user = null;$(document).ready(function(){ $("#chat").hide();//Ocultamos por defecto el layer del chat //controlamos el evento del boton de iniciar chat$("#btnJoin").click(function(){ socket = io.connect('http://localhost:8080');//iniciamos conexion con el server user = $("#user").val();//guardamos el usuario insertado en memoria socket.emit('login',user);//llamamos el evento login del server //Controlamos el evento cuando el servidor respondasocket.on('login', function (msg) { console.log('Received Login: %s', msg); }); //Llamamos el evento de list para obtener los usuarios conectadossocket.emit('list',"list"); //Controlamos la respuesta del serversocket.on('list', function (msg) { console.log('Received List: %s', msg); $(".leftSide").html(msg); }); //Controlamos el evento message que enviará el servidorsocket.on('message', function (msg) { console.log('Received Message: %s', msg); $(".rightSide").append(msg.text); }); $("#chat").show(); $(".user").hide();});$("#send").click(function(){ socket.emit('message',{"user": user,"message": $("#textSend").val()}); });});
node chat.jsPueden hacer la prueba entrando desde pestañas diferentes al puerto 8080 de su localhost, utilizando diferentes exploradores o si quieren hacerlo exponiendo la dirección en una red. A continuación como quedaron cada nuestros dos archivos:
chat.js
var socketio = require('socket.io'); var http = require('http'); var fs = require('fs'); var server = http.createServer(function (req, res) { res.writeHead(200, { 'Content-type': 'text/html' }); fs.createReadStream('./index.html').pipe(res); }); server.listen(8080); var users = [];//Con esto manejamos el listado de nuestros usuarios var io = socketio.listen(server);//Iniciamos la libreria con nuestro servidor//Controlamos el evento cuando se conecte e imprimimos en consola el mensaje recibido//emitiendo el evento hello hacia el cliente con un mensaje inicial. io.on('connection', function (socket) { socket.on('hello', function (msg) { console.log('Received: %s', msg); socket.emit('hello', 'Hello from server'); }); socket.on('login', function (msg) { console.log('Received Login: %s', msg); socket.emit('login', {"user": "System","text": "Welcome to the chat"}); users.push(msg); });//Por defecto enviamos un mensaje de bienvenida para el evento messagesocket.emit('message', '<p class="message">Welcome from server!</p>');//Controlamos el evento message y replicamos el mensaje a todos los clientes//que se encuentren conectados socket.on('message', function (msg) { console.log('Received message: %s', msg); var text = '<p class="message"><b>'+msg.user+'</b>:'+msg.message+'</p>'; io.sockets.emit('message', {"user": msg.user,"text": text}); });//Controlamos el evento list que nos va servir para ver el listado de usuarios//conectados socket.on('list', function (msg) { console.log('Received list: %s', msg); socket.emit('list', users); });});
index.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>WebSockets Chat Example</title> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"> </script> <script src="/socket.io/socket.io.js"></script>
<script>
//Inicializamos nuestras variables
var socket = null; var user = null;$(document).ready(function(){ $("#chat").hide();//Ocultamos por defecto el layer del chat //controlamos el evento del boton de iniciar chat$("#btnJoin").click(function(){ socket = io.connect('http://localhost:8080');//iniciamos conexion con el server user = $("#user").val();//guardamos el usuario insertado en memoria socket.emit('login',user);//llamamos el evento login del server //Controlamos el evento cuando el servidor respondasocket.on('login', function (msg) { console.log('Received Login: %s', msg); }); //Llamamos el evento de list para obtener los usuarios conectadossocket.emit('list',"list"); //Controlamos la respuesta del serversocket.on('list', function (msg) { console.log('Received List: %s', msg); $(".leftSide").html(msg); }); //Controlamos el evento message que enviará el servidorsocket.on('message', function (msg) { console.log('Received Message: %s', msg); $(".rightSide").append(msg.text); }); $("#chat").show(); $(".user").hide();});$("#send").click(function(){ socket.emit('message',{"user": user,"message": $("#textSend").val()}); });}); </script> <style> .leftSide{ float:left; min-width:25%; min-height:400px; margin:10px; padding:15px; border:1px solid #333;} .rightSide{ float:right; min-width:65%;min-height:400px; margin:10px; padding:15px;border:1px solid #333; } </style> </head> <body> <h2>Welcome to the WebSockets Chat</h2> <section class="user"> <label>User Name </label> <input type="text" id="user"/> <input type="button" value="Join" id="btnJoin"/> </section> <section id="chat"> <section class="leftSide"></section> <section class="rightSide"></section> <section> <input type="button" value="Send" id="send" style="margin:10px; padding:10px;" /> <input type="text" id="textSend" style="margin:10px;"/> </section> </section> </body> </html>
Conclusiones
Node.js es un lenguaje que esta avanzando muy rápido y nos permite realizar aplicaciones realmente emocionantes para tiempo real y muchos mas escenarios. El ejemplo que realice no tiene en cuenta detalles de estilos, validaciones o rendimiento, solo fue un ejemplo básico de como podemos utilizar las herramientas que nos brinda Node.js para hacer aplicaciones con WebSockets.
Si te sirvió esta guía por favor deja un comentario y si no te sirvió también.
Excelente Articulo Fabian
ResponderBorrarBuen post, gracias por el aporte Fabián.
ResponderBorrarBuena iniciativa Saludos y exitos
ResponderBorrarExcelente, podrias indicarme un ejemplo de control de sesiones cuando un usuario pasa a inactivo por tiempo o por que cierra la session
ResponderBorrar