Archivo por días: 1 noviembre 2005

webchat en tiempo real con php y mysql

Después de un pequeño descanso volvemos a las andadas. Esta vez voy a mostrar como crear un webchat en tiempo real.

El problema de los webchat es que la inmensa mayoría de los que existen se dedican a refrescar la página de la charla cada X tiempo o cuando el usuario ha escrito un mensaje. Esto es debido a que el protocolo HTTP esta diseñado para que una vez recibido los datos cierre la conexión, y si queremos recuperar datos nuevos tenemos que hacer una nueva petición.

Para lograr una interactividad y un realismo temporal cercanos al irc, messenger, etc. y seguir usando la web tenemos que usar un truco, y es crear una página que nunca termine de ejecutarse. Quizá os pregunteis que si nunca termina de ejecutarse, entónces no saldrá ningún resultado, pues bién, podemos desactivar el buffer del php desde el principio o hacer un volcado continuo para que cada dato nuevo se vaya enviando al navegador del cliente. Los navegadores muestran el contenido según les va llegando, por lo que una sentencia como esta (en pseudocodigo):

mientras verdadero
recupera mensajes nuevos
muestra mensajes nuevos
fin del mientras

haría que cada vez que un mensaje nuevo apareciese se enviase al navegador y este lo mostrara instantaneamente, con lo que habríamos conseguido que fuese el tiempo real.

Parar lograr este planteamiento en php tenemos que seguir los siguientes pasos (he reducido su código para simplificarlo):

ob_start();

Que activa el tratamiento del buffer en php

set_time_limit(0);

Indica al motor de php que el script nunca debe caducar a pesar de lo que dure.


for(;;)
{

Este es el bucle que constantemente se va repitiendo.


$rs=mysql_query(«SELECT * FROM chat_mensajes WHERE IDMensaje>'» . $id . «‘ AND WEEK(DatFecha,1)=WEEK(NOW(),1) AND YEAR(DatFecha)=YEAR(NOW()) ORDER BY IDMensaje»)
or die («No se puede realizar la consulta»);

En mi caso guardo los mensajes en una base de datos mysql, por lo que con la consulta SQL compruebo aquellos mensajes cuyo ID sea mayor que el último ID de mensaje que se ha mostrado (solo aquellos dentro de la semana actual del año en curso).


if(mysql_num_rows($rs)>0)
{
while($fila=mysql_fetch_array($rs,MYSQL_ASSOC)) {

Compruebo si hay nuevos mensajes y los recorro.


$id=$fila[«IDMensaje»];

Voy actualizando la variable id para que contenga al final el último ID y así en la siguiente pasada del bucle solo recoger los últimos mensajes escritos a partir del indicado.


echo «<div>\n»;
echo «<span>» . $fila[«DatFecha»] . » » . «</span>»;
echo «<span>» . $fila[«StrUsuario»] . «&gt;</span>»;
echo «<span>» . $fila[«StrMensaje»] . «</span>»;
echo «</div>\n»;
}

Se va mostrando el contenido de los mensajes (fecha, remitente y mensaje).


echo «<script>scrollTo(0,999999999);</script>\n»;
ob_flush();
flush();
}

Con un pequeño truco en javascript logramos desplazar la ventana del navegador siempre hasta el final del contenido. A continuacuón vaciamos el buffer hacia el navegador del cliente.


sleep(1);
}

Para no recargar mucho el servidor mysql haciendo constantemente consultas, pausamos durante un segundo la ejecución para después continuar con la siguiente iteración del bucle.

Quizá penseis que esta solución recarga mucho el servidor (tanto el web como el de mysql), pero el impacto es mínimo y si teneis un hosting esto os ahorrará ancho de banda puesto que gasta más por usuario el estar haciendo peticiones al servidor web cada X tiempo que una sola petición que dure eternamente (solo gasta lo que envia, como una petición normal); en cuanto al servidor mysql si este esta en localhost (como el 99% de los hosting) el impacto es mínimo porque no consume ningún recurso de red, además de que el propio mysql es rapídisimo y eficiente haciendo las consultas y no mermará el rendimiento por estar haciendo una cada segundo por cada usuario. Sin embargo si no quereis usar mysql, podeis usar ficheros (fopen) o bién memoria compartida (Shmop). Por supuesto esto no es la panacea, pero para cosas pequeñas (20 personas a la vez) si puede ir bién tenerlo así montado.

En la web en la que colaboro tengo implementado este sistema, que aunque lo tengo bastante desarollado, todavía tengo que mejorar ciertos aspectos de funcionalidad.