XMLHttpRequest responseText nem ürül

XMLHttpRequest responseText nem ürül
2014-06-12T04:28:51+02:00
2014-06-12T15:37:50+02:00
2022-10-15T22:30:59+02:00
sysoperator
Üdv mindenkinek!

Chatet írok, valami Comet-hez hasonló megoldást kerestem. Rendben is van minden, egy dolog kivételével.
Sokáig vadásztam a jó megoldásra, próbálkoztam az event-source-szal, meg egyéb variációkkal, de végül az XHR vált be.
Tudom, hogy a long pollingot nem szereti az Apache, de olyan oldalra lesz, ahol csúcsidőben 5-en vannak.
A kód lényegi része a Professional Ajax könyből van.

A program azt csinálja, hogy felszól a szervernek, az pedig 2 másodperceként irkálja ki mások üzeneteit (nincs a kódban), ugyanazon a kapcsolaton. Közben (a szerver) másodperceként elindít egy JS függvényt, ami 10 másodperceként tárcsázza a szervert, ha megszakadna a kapcsolat ( heartbeat() függvény ). 15 másodperc múlva pedig kiírja a pontos időt, ezzel szimulálva, hogy én üzenetet írtam a chatre. Ekkor a szerver bontja a kapcsolatot és az egész újra kezdődik.

És ez mind rendben is van, nagyon jól működik. Az egyetlen probléma azzal van, hogy a responseText tartalmához mindig hozzáfűződik az új string. Próbálkoztam már mindennel, próbáltam törölni, próbálkoztam az XHR újra példányosításával, de nincs hatással rá.
Egyedül akkor ürül a responseText, ha abort()-tal lezárom a kapcsolatot, de akkor újra tárcsáznom kell a szervert, hogy új kapcsolatom legyen. Kerestem neten, hogy más is találkozott-e már ilyennel, de semmit se találtam.

Kliens (görgess)

<div id="divStatus">Waiting for first message...</div> <script type="text/javascript"> //<![CDATA[ var oXHR = null; var iTimeoutId = null; $(document).ready(function(){ oXHR = new XMLHttpRequest(); startStream(); }); function heartbeat(){ clearTimeout(iTimeoutId); iTimeoutId = setTimeout(startStream, 10000); } function startStream(){ oXHR.abort(); oXHR.open('get', '?p=chat&a=chatd&ajax'); oXHR.onreadystatechange = function () { switch(oXHR.readyState) { case 3: var commands = oXHR.responseText; console.log(commands); eval(commands); break; case 4: startStream(); break; } }; oXHR.send(null); heartbeat(); } function modifiedAt(sDateTime) { document.getElementById("divStatus").innerHTML = "Modified at " + sDateTime; } //]]> </script>
Szerver

<? function chatd($db){ header("Content-type: text/javascript"); while (true) { echo ';heartbeat()'; ob_flush(); flush(); sleep(1); if(time() % 15 == 0){ echo ';modifiedAt("'. date('H:i:s'). '") ;startStream()'; ob_flush(); flush(); sleep(1); } } } ?>

A kliensben van egy console.log() a responseText számára, ez a kimenete:

;heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat()
;heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();heartbeat();modifiedAt("04:23:30") ;startStream();heartbeat()

Egyszerűen fogalmazva 1 válasznál 1 heartbeat()-nek kéne lennie, valamint a 15 mp elteltével "heartbeat();modifiedAt("04:23:30") ;startStream();"-nek kéne lennie, és nem tudom, hogy tudnám menet közben kipucolni a responseTextet.

A probléma végül is egyszerű, csak nehéz volt elmagyarázni.  Köszönöm annak, aki segít.
Mutasd a teljes hozzászólást!
Szerintem amit látsz, az teljesen elvárt működés. A PHP kódodhoz egy darab HTTP kérés érkezett, amire te egy darab HTTP választ küldesz, csak mesterségesen lelassítod a válasz generálását. A böngésző tájékoztat téged időnként a letöltés folyamatáról, a responseText-ben pedig megmutatja azt, amit eddig látott a válaszból, hiszen egy darab válaszról van szó. Ha a responseText-ben nem mutatná a válasz korábbi részét, pont az lenne a hibás működés.

A Transfer-Encoding header sem fog segíteni ebben az esetben. (Az is lehet, hogy már automatikusan chunked encoding-ot használ a szerver kérés nélkül is.) Ezt a header-t arra találták ki, hogy úgy is tudj adatokat küldeni, hogy az adatküldés kezdetekor még nem ismered az adat hosszát. (Ha ismered, akkor elküldheted a Content-Length header-ben, és chunked encoding nélkül is lehet tudni, hol a válasz vége.)

Valóban jobb lenne valami alkalmasabb technológiát használni, WebSocket-et vagy Server Sent Events-et. Szóba jön még a "rendes" long polling, ahol ha jött adat, azt elküldöd válaszként, és rendesen lezárod a választ, aztán a kliens egyből küld egy új kérést. Vagy ha ez se tetszik, akkor amit Frostech0 mondott: tárold le az előzőleg látott responseText-et, és képezd a különbséget a mostanihoz képest..
Mutasd a teljes hozzászólást!

  • Ha jól értem a kódodat, akkor amit te keresel, az a Transfer-Encoding: chunked - régebben volt egy téma itt a prog.hu-n ahol felmerült, hogy az IE nem nagyon támogatja, sajnos a témát azóta törölték, pedig volt benne egy egész jó kis példa (este megnézem hátha meg van még lokálban).

    Szóba jöhet még az SSE (Server-Sent Events). Ezt is érdemes megnézni - az IE ezt sem támogatja...

    ui: esetleg megnézheted, hogy az előző oXHR.responseText és az aktuális oXHR.responseText különbségét - pl: oXHR.responseText.substr(elozo.length) -et olvasod ki. De ez csak egy hirtelen jött tipp - nem próbáltam.
    Mutasd a teljes hozzászólást!
  • Szerintem amit látsz, az teljesen elvárt működés. A PHP kódodhoz egy darab HTTP kérés érkezett, amire te egy darab HTTP választ küldesz, csak mesterségesen lelassítod a válasz generálását. A böngésző tájékoztat téged időnként a letöltés folyamatáról, a responseText-ben pedig megmutatja azt, amit eddig látott a válaszból, hiszen egy darab válaszról van szó. Ha a responseText-ben nem mutatná a válasz korábbi részét, pont az lenne a hibás működés.

    A Transfer-Encoding header sem fog segíteni ebben az esetben. (Az is lehet, hogy már automatikusan chunked encoding-ot használ a szerver kérés nélkül is.) Ezt a header-t arra találták ki, hogy úgy is tudj adatokat küldeni, hogy az adatküldés kezdetekor még nem ismered az adat hosszát. (Ha ismered, akkor elküldheted a Content-Length header-ben, és chunked encoding nélkül is lehet tudni, hol a válasz vége.)

    Valóban jobb lenne valami alkalmasabb technológiát használni, WebSocket-et vagy Server Sent Events-et. Szóba jön még a "rendes" long polling, ahol ha jött adat, azt elküldöd válaszként, és rendesen lezárod a választ, aztán a kliens egyből küld egy új kérést. Vagy ha ez se tetszik, akkor amit Frostech0 mondott: tárold le az előzőleg látott responseText-et, és képezd a különbséget a mostanihoz képest..
    Mutasd a teljes hozzászólást!
  • Köszönöm mindkettőtöknek! Csaboka2-nek van igaza, hogy én miért nem jöttem rá erre, nem tudom...

    A WebSockettel az a baj, hogy a shared hostingok nem engedik, az SSE meg nálam meg se mozdult, pedig azt egy az egyben a Professional Ajax könyvből másoltam.
    A rendes long polling ha jól értem, ebben az esetben nem jó, mert minden 2. másodpercben jöhet adat, ennyi erővel csinálhattam volna egy frissülő iframe-t is.
    Viszont ez az XHR-es megoldás nagyon bevált, minden böngészőn működik, nem forog a throbbler, és egy nagyon szép, "folyamatos" kapcsolatot tart fenn, ami szolgáltatás kimaradás esetén is újra felépíti magát. 

    A responseText átalakítása/unique-zálása menni fog.

    Kösz még egyszer a választ az uraknak! 
    Ha valakinek lenne még hozzáfűznivalója, ne tartsa magában, érdekel a téma.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd