29 de Julio de 2010

Notas Espacio Programación

galería de imágenesvideo
AUTOR: Andrés Fernández
FECHA: 8/3/2009
LECTURAS:6256
Buscar Notas
volver
COMET / PSEUDOCOMET

Chatino: AJAX chat sin polling

Un ejemplo de uso de AJAX como alternativa para crear aplicaciones multiusuario.
Chatino es un chat de 7 kb de peso sin comprimir, basado en AJAX y PHP que, a diferencia de la mayoría de los chats de este tipo, no realiza requests a intervalos regulares al servidor para verificar updates (técnica conocida como polling). En lugar de eso, abre una conexión con en el servidor y la mantiene abierta en background en espera de que el servidor detecte alguna actualización que deba mostrarse. Esta modalidad, conocida como COMET o PSEUDOCOMET (en el verdadero esquema de COMET no es necesaria una reconexión después de los updates, pero esto no es soportado aún por la mayoría de los navegadores y termina supliéndose por lo que se conoce como long polling, que es la técnica que implementamos aquí), consume mucho menos ancho de banda que el modelo tradicional (polling).

ejemplo

Para poner en marcha chatino sólo necesitás subir el archivo chatino.php (que podés obtener usando el enlace de descargas ubicado en el lateral izquierdo de esta página) a un servidor que soporte php y tenga la funciones set_time_limit e ignore_user_abort habilitadas (la última sólo es necesaria para mostrar mensajes de desconexión y reconexión, pero el chat funcionará igualmente aunque no se encuentre disponible).
Otra cuestión importante: el directorio al que lo subas debe tener permisos de escritura.

Código utilizado:

<?php
session_start
();
set_time_limit(0);
if(!
file_exists('log.txt'))
    
file_put_contents('log.txt','1'."\n<span class=\"sistema\">Sala creada el ".date('d-n-y, H:i:s',time()).'</span>',LOCK_EX);
function 
escribir($t){
    
$numlineas=5;
    
$ant=file('log.txt');
    
$indice=intval(trim($ant[0]))+1;
    
$ant=implode('',array_slice($ant,0,($numlineas-1)*2));
    
file_put_contents('log.txt',$indice."\n".$t."\n".$ant,LOCK_EX);
}
function 
salidaAnunciada(){
    
$ver=file_get_contents('log.txt');
    
$sal=strpos($ver,$_SESSION['nick'].' ha salido de la sala.');
    if(
$sal!==false){
        
$en=strpos($ver,$_SESSION['nick'].' ha reingresado a la sala.');
        
$log=strpos($ver,htmlentities($_SESSION['nick'],ENT_QUOTES).' ha ingresado en la sala');
        if((
$en!==false && $sal<$en) || ($log!==false && $sal<$log))
            return 
true;
        return 
false;
    }
}
if(isset(
$_POST['nick']) && !empty($_POST['nick'])){
    
$_SESSION['nick']=utf8_decode($_POST['nick']);
    
setcookie('nick',utf8_decode($_POST['nick']),time()+36000,'/');
    
escribir('<span class="sistema">'.htmlentities($_SESSION['nick'],ENT_QUOTES)." ha ingresado en la sala.</span>");
    
header("Location:chatino.php");
    exit;
}
session_write_close();
header("Cache-control: private");
$vueltas=0;
ignore_user_abort(1);
if(isset(
$_POST['indice'])){
    while(
1){
        echo 
"\n";
        
ob_flush();
        
flush();
        if(
connection_status()!=0){
            if(!
salidaAnunciada())
                
escribir('<span class="sistema">'.$_SESSION['nick'].' ha salido de la sala.</span>');
            exit;
        }
        
$renglones=file('log.txt');
        
$i=trim($renglones[0]);
        if(
$_POST['indice']!=$i || empty($_SESSION['nick'])){
            echo 
utf8_encode(implode('<br />',file('log.txt')));
            
ob_flush();
            
flush();
            break;
        }
        if(++
$vueltas<20)
            
sleep(3);
        else
            
sleep(10);
    }
exit;
}
if(isset(
$_POST['w'])){
    
$_SESSION['nick']=empty($_SESSION['nick'])?$_COOKIE['nick']:$_SESSION['nick'];
    
$ver=file_get_contents('log.txt');
    
$sal=strpos($ver,$_SESSION['nick'].' ha salido de la sala.');
    if(
$sal!==false){
        
$en=strpos($ver,$_SESSION['nick'].' ha reingresado a la sala.');
        
$log=strpos($ver,htmlentities($_SESSION['nick'],ENT_QUOTES).' ha ingresado en la sala');
        if( (
$en===false && ($log===false)) || ($sal<$en && ($sal<$log || $log===false) ) || ($en==false && ($sal<$log || $log===false)) )
            
escribir('<span class="sistema">'.$_SESSION['nick'].' ha reingresado a la sala.</span>');
    }
    
escribir('<span class="user">'.$_SESSION['nick'].'</span>: <span class="mensaje">'.htmlentities($_POST['w'],ENT_QUOTES).'</span>');
    exit;
}
if(isset(
$_POST['out'])){
    if(!
salidaAnunciada())
        
escribir('<span class="sistema">'.$_SESSION['nick'].' ha salido de la sala.</span>');
    
session_start();
    
session_destroy();
    exit;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CHAT</title>
<style>
body{ font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;}
#login,#sender{ border:1px solid #000; background-color:#CCC;font-size:10px; width:50px}
#nick{font-size:10px; width:290px;border:1px solid #000;font-family:Verdana, Arial, Helvetica, sans-serif;}
#a{font-size:10px; width:380px;border:1px solid #000;font-family:Verdana, Arial, Helvetica, sans-serif;}
#mse{border:1px solid #000; width:360px; overflow:auto; padding:10px}
.user{color:#F00; font-weight:bold}
.mensaje{ color:#666}
.sistema{ color: #999}
h3{font-size:10px; padding:0;color:#999;}
</style>
<script>
function http(){
    if(typeof window.XMLHttpRequest!='undefined'){
        return new XMLHttpRequest();
    }else{
        try{
            return new ActiveXObject('Microsoft.XMLHTTP');
        }catch(e){
            alert('Su navegador no soporta AJAX');
            return false;
        }
    }
}
function request(url,callback,params){
    var H=new http();
    if(!H)return;
    H.open('post',url+'?'+Math.random(),true);
    H.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    H.onreadystatechange=function(){
        if(H.readyState==4){
            if(callback)
                callback(H.responseText);
            H.onreadystatechange=function(){}
            H.abort();
            H=null;
        }
    }
    var p='';
    for(var i in params){
        p+='&'+i+'='+escape(params[i]);
    }
    H.send(p);
}
function $(x){return document.getElementById(x);}
onload=function(){
    try{
        request('chatino.php',f,{'indice':0});
    }catch(e){}
}
function f(r){
    try{
        $('mse').innerHTML='';
        var txt=r.split('<br />').reverse();
        for(var i=0;txt[i];i+=2)
            $('mse').innerHTML+=txt[i]+'<br />';
        var indice=parseInt(r.split('<br />')[0]);
        if(isNaN(indice))return;
        setTimeout(function(){
            request('chatino.php',f,{'indice':indice});
            },10);
    }catch(e){}
}
function ver(e){
    var t=e.keyCode || e.wich;
    if(t==13){
        request('chatino.php',null,{'w':$('a').value});$('a').value='';
        return false;
    }
    return true;
}
</script>
<?php
if(isset($_SESSION['nick']) && !empty($_SESSION['nick'])){
?>
<!--[if IE]>
<script>
onbeforeunload=function(){
    request('chatino.php',null,{'out':1});
}
</script>
<![endif]-->
<?php ?>
</head>
<body>
<?php
if(!isset($_SESSION['nick']) || empty($_SESSION['nick'])){
?>
<h3>Complete el formulario para ingresar a la sala:</h3>
<form action="chatino.php" method="post">
  Nick:
  <input name="nick" type="text" id="nick" />
  <input id="login" type="submit" value="Enviar" />
</form>
<?php }else{?>
<div id="mse"></div>
<form>
  <textarea cols="60" rows="1" id="a" onkeypress="return ver(event)"></textarea>
  <input id="sender" type="button" value="Enviar" onclick="request('chatino.php',null,{'w':$('a').value});$('a').value=''" />
</form>
<?php }?>
</body>
</html>

Home - Quiénes Somos - Portfolio - Espacio Diseño - Espacio Programación - Capacitación - Contacto - RSS - XHTML 1.0