Vývoj webového online chatu

plastik

Vývoj webového online chatu
« kdy: 25. 05. 2013, 20:28:52 »
ahoj jak vytvořit efektivní online chat ??? napadlo mě že bych v intervalech vysílal pomocí ajaxa požadavky do php souboru a pokud by v databázi nebo v nějakém serverovém souboru objevil nový zápis tak by ho poslal a javascript by ho pak zase zobrazil. Dále mě napadlo že intervaly by v podstatě mohl vysílat i samotný serverový php skript (pomocí funkce sleep) a zátěž by přešla z klienta na server nicméně stejně by tam byla. Nejlepší by bylo vyřešit to pomocí událostí nicméňě nevím jestli se to dá vůbec nějak zapsat. Máte někdo radu?? 
« Poslední změna: 25. 05. 2013, 22:51:14 od Petr Krčmář »


sss

Re:online chat
« Odpověď #1 kdy: 25. 05. 2013, 21:14:24 »
Zkus kouknout na tohle: http://www.installfest.cz/if13/archiv - příspěvěk Real-time web by Adam Hořčica

Michal Taneček

Re:online chat
« Odpověď #2 kdy: 25. 05. 2013, 21:22:42 »
Klasické "pingování" je sice možnost, ale lepší je využít websockety, nebo socket.io. Tyto technologie umožňují XHR polling (neplést s pooling). Vtip je v tom, že nastavíte timeout XHR requestu třeba na minutu, ten na serveru "zmrazíte" a až bude co zobrazovat, tak pošlete data. (Po timeoutu vytvoříte nový request) Reakce je okamžitá a máte realtime chat, bez zbytečného overheadu. Nevím jestli se toto dá řešit v PHP, já toto používám v nodeJS. (třeba Nette framework má určité metody freeze,store nad http requestem, ale ty jsem nestudoval)

Po zkušenostech doporučuji nenechat zkončit request na timeoutu, ale pár sekund před vypršením ho ukončit a nechat lienta vytvořit nový.

NodeJS by jsem se nebál, taky píšu weby v PHP, to je prostě pohodlnost... Pouze určité rutiny mám v nodeJS.

tomas

Re:Vývoj webového online chatu
« Odpověď #3 kdy: 26. 05. 2013, 10:55:35 »

michi

Re:Vývoj webového online chatu
« Odpověď #4 kdy: 26. 05. 2013, 11:43:13 »
Jednoduchý example v Ruby (server) HTML/JS (client).

server:
Kód: [Vybrat]
require "socket"
require "thread"

threads = [ ]
messages = [ ] # simpmle array of strings
mut = Mutex.new

$port = 2000
webserver = TCPServer.new('127.0.0.1', $port)
puts "Starting listening on port #{$port}..."

Thread.new {
   while true
      sleep 5
      mut.synchronize { threads.each{|t| threads.delete t if t.status == false } }
   end
}

while ($session = webserver.accept)

   mut.synchronize {

      # Forking data and processing
      session = $session

      t = Thread.new {

         request = session.gets
         reply = ''
         puts "Request: #{request}"

         if request.index('check')

            sleep 5 if messages.count == 0
            m = messages.join('","')
            m = '"'+ m + '"' if m.length > 0
            reply = '{"messages":['+ m +']}'
            messages.clear

         elsif request.index('post')

            reply = '{"status":"ok"}'
            threads.each{|t| t.wakeup if t.status == 'sleep' }
            messages << "Hi"

         else
            reply = '{"error":"Bad request"}'
         end

         session.print "HTTP/1.1 200/OK\r\n"
         session.print "Content-Length: #{reply.length}\r\n"
         session.print "Access-Control-Allow-Origin: *\r\n"
         session.print "Content-Type: text/html\r\n\r\n"
         session.print reply
         session.close
      }

      # Joining and continuing...
      threads << t
   }
end

puts "Shutting down..."

klient:
Kód: [Vybrat]
<html>
<head>
   <title>REST test</title>
   <script type="text/javascript">

var last_check = 0
var last_error = false

function check()
{
   if (Date.now() < last_check+5000 && last_error) {
      setTimeout(function(){check()}, 5000);
      return;
   }
   last_check = Date.now();
   var req = new XMLHttpRequest();
   req.open("GET","http://localhost:2000/?check",true);
   req.onreadystatechange = function() {
      if (req.readyState==4 && req.status==200) {
         var text = req.responseText;
         var c = document.getElementById('console');
         c.innerHTML += text;
         setTimeout(function(){check()}, 100);
         last_error = false;
      }
      else if (req.readyState==4 && req.status==0) {
         setTimeout(function(){check()}, 5000);
         last_error = true;
      }
   }
   req.send();
}

   </script>
</head>
<body onload="check()">
   <div id="console"></div>
</body>
</html>

Tenhle příklad počítá na serveru se dvěma požadavky, jeden zařazuje fixní zprávu do fronty, druhý kontroluje frontu, pokud v ní něco je, pokračuje hned, v případě prázdné fronty vlákno na chvíli uspí. Pokud mezitím v jiném vlákně přijde nová, probudí se a okamžitě pošle odpověď, jinak se pošle odpověď s prázdným polem. Šlo mi jen o vláknování a reakci probuzení při změně stavu, plnou funkcionalitu tam nehledej.

Vcelku snadno to jde ale rozšířit frontu o vlákna pro jednotlivé uživatele, zakomponování vlastní zprávy atd. Vyloženě se na toto hodí nějaký „vlastní“ způsob (proto Ruby listener na jiném portu), s použitím webserveru/skriptu přes CGI bys nejspíš nepochodil, primárně kvůli tomu, že se tím v drtivé většině zpracovává každý požadavek ve vlastním anonymním vlákně a někam výš se dostane jen těžko.