Ajax, PHP Long Polling ohne JQuery

cooldiman1

Lt. Junior Grade
Registriert
Dez. 2011
Beiträge
299
Hallo CB-Freunde,

ich arbeite zurzeit an einem Chat.
Dieser aktualisiert sich momentan immer alle 4 Sekunden.

Das wollte ich jetzt durch Long Polling ersetzen.

Ich denke ich habe verstanden wie es allgemein funktioniert aber nicht wie ich das jetzt umsetze ohne JQuery.

Hier ein paar Quelltextausschnitte:
Ajax Teil:
Code:
function reload(chat_id, parameter1, numberofdata, addition1)
{

	switch (parameter1)
	{
		case "chat_overview":
		
			ajax(addition1 , "", "", "check_new_entry", "privat", "", numberofdata); // Checken 
			
		break;
		
		case "user_chat":
		
			ajax("" , chat_id, "", "one_chat_reload", "", "", numberofdata);
			
		break;
		
		case "public_chat":
		
			ajax(addition1 , "", "", "check_new_entry", "public", "", numberofdata); // Checken
			
		break;
		
	}
	
	jscheck(1);
	
	setTimeout	
	(
		function() 
		{
			reload("", parameter1, numberofdata, addition1);
		}
	, 4000);
}


function ajax(username, chat_id, text, parameter, addition1, addition2, numberofdata)
{
	var obj;
	
	if (window.XMLHttpRequest)
		obj = new XMLHttpRequest();
	else
		obj = new ActiveXObject("Microsoft.XMLHTTP");

	switch (parameter)
	{
		case "public_chat_reload":
			obj.open("POST","ajax_php_functions/public_data_load.php");
			var data_params = "chat_id="+chat_id+"&first_call="+first_call+"&numberofdata="+numberofdata+"&last_reload="+last_reload;
		break;
	}

	obj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	obj.setRequestHeader("Content-length", data_params.length);
	obj.setRequestHeader("Connection", "close");
	obj.send(data_params);
	obj.onreadystatechange = loading_data;
	
	function loading_data()
	{
		if (obj.readyState!=4)
		{
			//alert(obj.responseText);
			var output = obj.responseText;
			document.getElementById("check_4").innerHTML=output;
		}
                if (obj.readyState==4 && obj.status==200)
		{
                      // Ende
                }
        }

PHP Teil:
PHP:
        sleep(1);
	echo "test";
	sleep(4);

So weiter weiß ich nicht. Bisher habe ich versucht das nach dem erstem Sleep das "Test" ausgeben wird und dann nach dem 2.tem Sleep die Anfrage beendet wird.

Kann mir jemand vielleicht ein kleines Beispiel zeigen oder es selber kurz erstellen. Ich würde das wirklich sehr gerne verwenden da es bestimmt viel eleganter ist anstatt alle 4 Sekunden eine Ajax funktion aufzurufen :)

Google ist leider irgendwie nicht ganz so hilfreich oder ich suche einfach nach den falschen Begriffen :)

Gruß,
cooldi
 
Zuletzt bearbeitet:
cooldiman1 schrieb:
So weiter weiß ich nicht. Bisher habe ich versucht das nach dem erstem Sleep das "Test" ausgeben wird und dann nach dem 2.tem Sleep die Anfrage beendet wird.
Ist ja auch richtig so nach dem, was du uns hier zeigst. Was willst du denn anders haben?
cooldiman1 schrieb:
Kann mir jemand vielleicht ein kleines Beispiel zeigen oder es selber kurz erstellen. Ich würde das wirklich sehr gerne verwenden da es bestimmt viel eleganter ist anstatt alle 4 Sekunden eine Ajax funktion aufzurufen :)
Du machst nen Request, das Script arbeitet prinzipiell "ohne Zeitlimit" (Stichwort Endlosschleife; ein Ende brauchst du aber natürlich) und sobald ein Response da ist, wird dieser ausgewertet und prompt ein neuer Request abgeschickt.

Bebildert:
http://stackoverflow.com/questions/...g-websockets-server-sent-events-sse-and-comet
 
Ich wollte dass die Verbindung offen bleibt und das PHP Script später in eine Endlosschleife läuft. Sodass nur ein Ajax Verdingung geöffnet wird und diese für länger offen bleibt.

Mein Problem wenn ich "alert(obj.responseText);"
Also das hier:
Code:
if (obj.readyState!=4)
{
//alert(obj.responseText);
var output = obj.responseText;
document.getElementById("check_4").innerHTML=output;
}

Dann kommt aber nach einer Sekunde nicht der Text "Test" an.

Muss das PHP Script erst komplett durchgelaufen sein bei der Ajax abfrage ?
 
Zuletzt bearbeitet:
Als Beispiel auf Client-Seite: http://stackoverflow.com/questions/21789425/simple-xhr-long-polling-without-jquery Du setzt im Ajax-Request eigentlich nur den Timeout entsprechend hoch. In OnTimeout-Event rufst du faktisch die Polling-Funktion nochmal auf, ebenso wenn der Content geladen (also ein Response zurück kam) und verarbeitet wurde.

Auf PHP-Seite, machst du "nur" eine Endlosschleife.
Code:
<?php

while( true )
{
	$daten_vorhanden = /* ... */;
	if( $daten_vorhanden )
	{
		$daten = hole_daten();
		$daten = daten_formatieren( $daten );
		echo $daten;
		break;
	}
	usleep( 2500 ); // warte ein Weilchen und hier nicht den Server komplett auslasten
}
daten_formatieren() kannst du eigentlich durch ein json_encode() ersetzen, damit du auf Client-Seite gleich die passende Formatierung hast.
 
Warum nicht zeitgemäß mit Websockets? Entsprechende libs wie SockJS haben bereits fallbacks (wie long polling) für Holzbrowser integriert.
 
ok Ich habe jetzt alles etwas angepasst:

PHP sieht vorerst so aus:
PHP:
$x = 0; // Zähler variable
		
while (true)
{
      $sql = $GLOBALS['db']->query("SELECT public_new_entry FROM check_new_entry WHERE             user='".$username."'");
	
$check = $sql->fetch_array(MYSQLI_ASSOC);
				
if ($check['public_new_entry'] == 1) // Falls neuer eintrag vorhanden ist
{
		$c = $GLOBALS['db']->query("update check_new_entry
				set public_new_entry = 0
				where user='".$username."'") 
                               OR die("Error: ".mysql_error());
					
		echo "new_entry";
		break;
	}
	$now = time();
				
	if(($now-$zeit)>10) // Nach 10 Sekunden beenden
	{
		echo $x; // Durchläufe
		break;
	}
				
	usleep(200000);
	$x++;
}


Und bei Ajax hab ich auch ein wenig rumprobiert und einfach nur das hinzugefügt:

Code:
obj.timeout = 8000;
	obj.ontimeout = function()
	{
		document.getElementById("check_4").innerHTML="ende";
	}

Das Problem dass das Script läuft und ich keinen weiteren Tab von meiner Seite öffnen kann. Das PHP Script blockiert den Server leider irgendwie denke ich.

Danke für eure Hilfe :)

Gruß,
cooldi


EDIT:

Hab es gelöst. Man muss das "session_start();" entfernen da sonst der Server weitere Anfragen blockiert die mit der Session zu tun haben. Denke das ist richtig gesagt :)
Hatt nämlich noch ein session_start(); oben im Script drinne was von Ajax abgerufen wird. Ich weiß ihr konntet das nicht sehen :( Sry

Aber jetzt geht´s :D
Ergänzung ()

Hallo CB-User,

ich habe eine weiteres Problem.

Ich hab das gefühl, wenn ich die Ajax Abfrage abbreche oder sie abläuft dann läuft die PHP Schleife weiter und wird nicht abgebrochen.
Der Server ist nämlich gerstern wegen überlastung krepiert.

Weiß jemand mehr?

Gruß,
cooldi
 
Zuletzt bearbeitet:
Das tut es aber nachweißlich nicht.

Ich habe jetzt die schleife so angepasst um zu schauen ob sie weiterläuft:

Hier PHP:
PHP:
$x = 0;
$zeit = time();

while ($ende != true && $x < 10 && ($now-$zeit)<10)
{
	$now = time();
				
	if ($x > 10)
	{
	      break;
              $ende = true;
	}
	sleep(1);
	$x++;
				
	$c = $GLOBALS['db']->query("update test
		set test = ".$x."
		where user='".$username."'") 
              OR die("Error: ".mysql_error());
}

In der Tabelle Test wird immer jede Sekunde $x reingeschrieben

Das Ajax obj.timeout = 2000; (steht auf 2 Sekunden)

Somit müsste ja $x ja nur 2 erreichen. Vielleicht drei wenn der Befehl zum Server zu langsam ist aber dann sollte in der Tabelle Test nichts mehr passieren.

Ich beobachte also per Firebug und sehe das TimeOut funktioniert nach 2 Sekunden und beendet die Anfrage.
ABER der Wert in der Datenbank erhöht sich noch weiterhin und $x wird weiterhin eingetragen. Bis er den Wert 10 erreicht und die Schleife endet.

Somit läuft das PHP Script weiter. Ich habe gedacht das es aufhört und somit habe ich wie oben auch gezeigt Endlosschleifen eingebaut gehabt die den Server dann gestern noch im Laufe der Zeit lahmgelegt haben.
 
Zuletzt bearbeitet:
Das Timeout des Ajax-Queries entspricht nicht dem Script Timeout in PHP. Ich weiß nicht inwiefern ignore_user_abort() funktioniert, da ich es noch nie genutzt habe, allerdings hat ein PHP-Script eine Standardlaufzeit von 30 Sekunden. Da kann das Timeout deines Ajax-Requests egal wie niedrig sein, das PHP Script wird trotzdem 30 Sekunden laufen und erst danach abbrechen. Ergo synchronisierst du die Laufzeiten, findest evtl. eine zuverlässige(re) Funktion oder du musst die Scripts nun mal auslaufen lassen. Du kannst serversetig ja auch mehrere Requests unterbinden, wenn der andere noch am Laufen ist bzw. einen gewissen Schwellwert noch nicht unterschritten hat.
 
Habe mich auch gerade eben damit beschäftigt und leider beendet das PHP auch nicht nach 30 Sekunden.

Auch ein Limit manuell zu setzen hat nicht geklappt.

Was aber geht ist innerhalb der Schleife ein ausgabe zu machen. Z. B. "echo "hello";". Dann funktioniert das TimeOut von Ajax und das Script stoppt wenn der Timeout von Ajax einsetzt.

Ich denke es hat irgendwas damit zu tune dass durch das "echo" irgendwie PHP sieht dass die Ausführung beendet werden soll und dann tut es das auch.

Irgendwie verwirrend das Ganze :)
 
cooldiman1 schrieb:
Was aber geht ist innerhalb der Schleife ein ausgabe zu machen. Z. B. "echo "hello";". Dann funktioniert das TimeOut von Ajax und das Script stoppt wenn der Timeout von Ajax einsetzt.

Es funktioniert mit dem echo, da PHP auch erstmal erkennen muss, dass der HTTP Request abgebrochen wurde. Das ist eben standardmäßig nicht der Fall, also per FastCGI oder ähnlichem wird PHP nicht über den Abbruch informiert, versucht PHP aber mit einem echo dem Webserver Daten zu geben, die dieser dem Client sendet wird das natürlich fehlschlagen, und darüber wird PHP dann infomiert und kann das Script abbrechen.

Und PHP wird sich nach 30sek beenden, wenn das Limit auch auf 30 Sekunde gesetzt wurde.
 
Hallo CB-User,

ich habe eine weiteres Problem.

PHP Teil der per Ajax aufgerufen wird.
PHP:
$array[0] = "an";
echo json_encode($array);

while ($ende != true && ($now-$zeit)<45)
  {
	$now = time();

	$sql = $GLOBALS['db']->query("
        SELECT new_entry FROM check_new_entry WHERE user='".$username."'");
	$check = $sql->fetch_array(MYSQLI_ASSOC);		
		
        if ($check['new_entry'] == 1)
	{
		$c = $GLOBALS['db']->query("update check_new_entry
			set new_entry = 0
			where user='".$username."'") 
                        OR die("Error: ".mysql_error());
			
			$array[0] = "new";
		
			echo json_encode($array);
			//flush();
		}
		
		usleep(2000000);
	}

So ich habe für mich ein unlösbares Problem entdeckt.

Und zwar über der Schleife wird immer das "array" abgesendet. Immer. Innerhalb von Firebug kann ich sehen das etwas angekommen ist und kann es verarbeiten noch während die Anfrage von Ajax läuft. Glaub so ähnlich wie Facebook das auch tut.

So doch innerhalbt der Schleife geht die Ausgabe manchmal schief.

Das bedeutet dass wenn es einen neuen Eintrag gibt müsste er einmal etwas absenden.
Nämlich das"new" innerhalb der If-Abfrage.
Aber das funktioniert nicht immer. In der Datenbank wird 100%ig der abgefragte Wert auf 1 gesetzt und es gibt auch keine Fehler beim Eintragen oder auslesen. Das PHP-Script geht in die IF-Abfrage auch rein und setzt den Wert wieder auf 0.
Das klappt alles wunderbar.

Aber die echo Ausgabe klappt nicht. Man sieht bei Firebug auch das wirklich nichts ankommt.
Das kuriose ist nun das es manchmal klappt und manchmal nicht.

Mit dem ganzen:
fluch();
ob_end_flush();
ob_start();
ob_fluch();

hab ich auch schon rumexperimentiert.

Ich komme einfach nicht weiter.

Brauche wirklich Hilfe :)

Gruß,
cooldi
 
Zuletzt bearbeitet:
der Browser kann die Daten auch wieder cachen, und das ist bei denen sogar ziemlich eklig, du bekommt nicht jedes Byte sofort.
Schau dir mal Server Sent Events (SSE) an, das wird dein Problem lösen, dafür gibt es auch Polyfills wenn Browser es noch nicht unterstützen.
 
Zurück
Oben