El desarrollo de aplicaciones web que entreguen información actualizada y al momento es muy importante, existen técnicas y buenas prácticas que según sea el caso podemos emplear para desarrollar funciones en tiempo real, antes debemos tener en claro diferencias, conceptos, tipos, otras variantes y ejemplos para comprender bien este tópico, como nota interesante, a pesar de que el artículo lo abordamos en PHP, a lo largo del mismo veremos que es una repartición entre servidor y cliente para lograrlo, sin más preambulo, comencemos.
Requerimientos
Antes de tener en cuenta los ejecutables requeridos, importante mencionar que el sistema operativo es Windows.
- XAMPP
- Composer
- Cuenta en Pusher: https://dashboard.pusher.com/accounts/sign_up
Síncrono, Asíncrono y en Tiempo Real
En el desarrollo de aplicaciones web es importante entender que el sitio, las páginas y funcionalidades, son respuestas a peticiones hechas por el usuario, las cuáles conforme vamos practicando más, comenzamos a familiarizar y planteamos la pregunta de cómo funcionan.
En peticiones síncronas, cuando la aplicación web se ejecuta, ensambla contenido solicitando los recursos, por ejemplo la vista (HTML, CSS, JS, Imágenes, etc), cierto HTML preprocesado con fragmentos del lenguaje del servidor, por ejemplo PHP iteraciones foreach donde se vacían registros consultados a la Base de Datos en una tabla HTML, vacíado de registros de la Base de Datos en un select HTML, programación para recepción de formularios. El caso es que si necesitas la información actualizada deberás recargar la página, más que nada en el tema servidor, para saber si se agregaron, modificaron o eliminaron registros.
En peticiones asíncronas, cuando la aplicación web se ejecuta, ocurre lo mismo solo de lado del cliente, pero ahora se llaman en segundo plano el HTML preprocesado o mejor aún respuestas JSON para iterar con JavaScript y convertirlo en HTML reduciendo así la carga al servidor, la programación de los formularios ahora es con JavaScript, para impedir que recarguen la página y en cambio manden la información del formulario en segundo plano cuando se envíe el formulario.
El tiempo real, es una expresión en aplicaciones que nos indica que la información está al momento, solamente que va por optimización, en las peticiones síncronas los datos están frescos solamente cuando se recarga la página, en las peticiones asíncronas es el mismo caso, solamente que no es necesario volver a ejecutar todo, solamente se actualiza los recursos que correspondan, en este punto se va reduciendo la carga al servidor solo llamando peticiones específicas. La problemática de estos 2 tipos de peticiones, es que solo benefician a un usuario (El que agrega, modifica y elimina registros), mientras que los demás usuarios que quizas en el momento no están utilizando esas funciones quedan desactualizados, aquí es donde entra el concepto tiempo real, donde debemos usar WebSockets.
¿Cuándo debemos implementar WebSockets? Cuando nuestra aplicación tenga una gran cantidad de usuarios, los usuarios estén en diferentes lugares físicos o el registro de las acciones sea muy importante.
Hasta esta sección, al tocar el concepto peticiones, todavía existiría algo a tener en cuenta, muchas veces también las inacciones o los no cambios son muy importantes, y son información en tiempo real, para estos casos podemos usar funciones cronometradas (Se repiten por un determinado intervalo) de lado del cliente, ahorrando infraestructura, porque los websockets representan un costo significativo.
También notamos que hemos estado hablando de conceptos dentro de la aplicación, cuando incluso existen ejecuciones programadas sin necesidad de que el usuario esté usando la aplicación, estas se llevarán a cabo por programación cronometradas (Se repiten por un determinado intervalo), conocidas como CronJobs, tareas programadas, también representan un costo significativo.
Aplicaciones Web con Ratchet
Ratchet es una dependencia PHP que podemos descargar con composer, mediante nuestra terminal de Windows, nos posicionarnos en el proyecto, creamos una carpeta a la cual nombraremos ratchet y nos posicionamos en esa carpeta (Cambia prueba por el nombre de tu proyecto):
cd c:/xampp/htdocs/prueba/ratchet/
Después ejecutamos el siguiente comando:
composer require cboden/ratchet:^0.4.4
Si todo sale bien, se descargarán los archivos requeridos de la dependencia ratchet.
Comparto el siguiente ejemplo de chat, en algunos códigos he colocado un comentario muy importante para que funcione correctamente al editarlo según el caso.
c:/xampp/htdocs/prueba/ratchet.php
<?php
require "ratchet/vendor/autoload.php";
class MyChat implements Ratchet\MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(Ratchet\ConnectionInterface $conn) {
$this->clients->attach($conn);
}
public function onMessage(Ratchet\ConnectionInterface $from, $msg) {
foreach ($this->clients as $client) {
if ($from != $client) {
$client->send($msg);
}
}
}
public function onClose(Ratchet\ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(Ratchet\ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
// Por defecto el ejemplo proporciona el puerto 8080, pero si has tenido problemas con ese puerto solo cambialo por uno diferente al de tu localhost, en mi caso:
// mi app ----> localhost:81
// websocket -> localhost:8080
// No usar el mismo puerto de la app.
$app = new Ratchet\App("localhost", 8080);
$app->route("/chat", new MyChat, array("*"));
$app->route("/echo", new Ratchet\Server\EchoServer, array("*"));
$app->run();
c:/xampp/htdocs/prueba/ratchet.html
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chat</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-4.0.0.min.js" integrity="sha256-OaVG6prZf4v69dPg6PhVattBXkcOWQB62pdZ3ORyrao=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous" defer></script>
<script src="js/ratchet.js" defer></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 col-xxl-6">
<form id="frmMensaje" method="POST">
<div class="mb-1">
<div class="row align-items-end">
<div class="col-12 col-sm-12 col-md-12 col-lg-8 col-xl-8 col-xxl-8 order-3 order-sm-3 order-md-3 order-lg-1 order-xl-1 order-xxl-1">
<label for="txtUsuario">Usuario:</label>
<input type="text" id="txtUsuario" name="txtUsuario" class="form-control">
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2 col-xxl-2 order-1 order-sm-1 order-md-1 order-lg-2 col-xl-2 order-xxl-2">
<label for="colBg">Color Fondo:</label>
<input type="color" id="colBg" name="colBg" class="form-control form-control-color">
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-2 col-xl-2 col-xxl-2 order-2 order-sm-2 order-md-2 order-lg-3 col-xl-3 order-xxl-3">
<label for="colTexto">Color Texto:</label>
<input type="color" id="colTexto" name="colTexto" class="form-control form-control-color">
</div>
</div>
</div>
<div class="mb-1">
<label for="txtMensaje">Mensaje:</label>
<textarea name="txtMensaje" id="txtMensaje" class="form-control" rows="3"></textarea>
</div>
<div class="mb-1">
<button class="btn btn-primary">Enviar</button>
</div>
</form>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-6 col-xl-6 col-xxl-6">
<div class="div-chat" style="height: 300px; overflow-y: auto;"></div>
</div>
</div>
</div>
</body>
</html>
c:/xampp/htdocs/prueba/js/ratchet.js
function agregarMensaje(data) {
const mensaje = JSON.parse(data)
$(".div-chat").append(`<div style="background: ${mensaje.colorFondo}; color: ${mensaje.colorTexto};">
${mensaje.usuario ? mensaje.usuario + ": " : ""}
${mensaje.mensaje}
</div>`)
}
// Por defecto el ejemplo proporciona el puerto 8080, pero si has tenido problemas con ese puerto solo cambialo por uno diferente al de tu localhost, en mi caso:
// mi app ----> localhost:81
// websocket -> localhost:8080
// No usar el mismo puerto de la app.
const conn = new WebSocket("ws://localhost:8080/chat")
conn.onmessage = function (e) {
const data = e.data
console.log(data)
agregarMensaje(data)
}
conn.onopen = function (e) {
conn.send(JSON.stringify({
colorFondo: "yellow",
colorTexto: "black",
usuario: "",
mensaje: "Alguien se unió al chat."
}))
}
$("#frmMensaje").submit(function (event) {
event.preventDefault()
const mensaje = {
colorFondo: $("#colBg").val(),
colorTexto: $("#colTexto").val(),
usuario: $("#txtUsuario").val(),
mensaje: $("#txtMensaje").val()
}
const data = JSON.stringify(mensaje)
conn.send(data)
agregarMensaje(data)
$("#txtMensaje").val("")
})
Para ponerlo en marcha ejecutamos XAMPP, accedemos al localhost en nuestro proyecto en el archivo ratchet.php, en mi caso http://localhost:81/prueba/ratchet.php, puede marcarnos un error, que dependerá de la versión de XAMPP o si no se descargo correctamente la dependencia ratchet, y si no marca error, puede que se quede en un bucle, cargando de manera indefinida, eso quiere decir que sí funcionó, porque se encuentra en escucha de eventos, después de eso solamente en nuestra url cambiamos a ratchet.html, en mi caso http://localhost:81/prueba/ratchet.html.
Si los errores persisten, puedes mejor descargar el proyecto en github:
https://github.com/domingofhdz/practicasAWOS
Una vez hecho lo anterior, podemos poner a prueba el chat, abrimos el ratchet.html en 2 navegadores distintos, o el mismo navegador y una ventana incognita, no importa la opción seleccionada si todo está bien, funcionará correctamente.

Hay detalles por afinar, pero el ejemplo resulta útil a modo de introducción a este tipo de aplicaciones. Los siguientes pasos son añadir una regla al firewall del puerto del websocket:





Cambiar el localhost por la ip v4 local, la cuál puedes obtener en terminal por el siguiente comando:
ipconfig
Proximamente coloco aquí el artículo que continua.
Conclusión
El artículo se ha extendido demasiado para solo haber tocado 2 temas, más adelante procuraremos completar lo referente a la creación de aplicaciones web en tiempo real, comprensión de conceptos básicos, aplicaciones locales y externas, eventos programados síncronos y asíncronos, de momento el ejemplo brindado resulta suficiente para comprender el tema. Espero este post les sea de utilidad y estaré al tanto de cualquier comentario, gracias por su tiempo.