2017-01-17 5 views
3

Ich habe einen Knoten WebSockets Server alle eingerichtet mit einem Chat-Dienst funktioniert super. Aber ich möchte, dass ein LAMP-Server regelmäßig Nachrichten an Benutzer senden kann, die mit dem Websocket-Server verbunden sind. (Entweder als Antwort auf Benutzer-Browser-Aktionen oder Cron-Jobs). Also brauchte ich PHP-Code, um eine Anfrage an den Node-Server zu senden (beide auf Google Compute Engine).fsockopen Verbindung bleibt offen

ich über diese Antwort gefunden: https://stackoverflow.com/a/22411059/947374 ... ein Link zu einem Forum, in dem jemand diese ziemlich gut ausstieg gearbeitet: https://forum.ripple.com/viewtopic.php?f=2&t=6171&p=43313#p43313

Mein jetzt PHP-Code wie folgt aussieht:

//local address of node server 
$host='XXX.XXX.XXX.XXX'; 
$port=80; 
//location where THIS script is running FROM 
$local="http://localhost"; 
//json the data to send 
$data=json_encode($data); 

//some very particular headers 
$head = "GET/HTTP/1.1"."\r\n". 
     "Upgrade: WebSocket"."\r\n". 
     "Connection: Upgrade"."\r\n". 
     "Origin: $local"."\r\n". 
     "Host: $host"."\r\n". 
     "Sec-WebSocket-Version: 13"."\r\n". 
     "Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n". 
     "Content-Length: ".strlen($data)."\r\n"."\r\n"; 
//WebSocket handshake 
$sock = fsockopen($host, $port, $errno, $errstr, 2); 
fwrite($sock, $head) or die('error:'.$errno.':'.$errstr); 
$headers = fread($sock, 2000); 
//see the second link above for what hybi10Encode() is doing 
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr); 
$wsdata = fread($sock, 2000); 
fclose($sock); 

Die Daten wird von PHP an Knoten gesendet. Schön. Preiset den Herrn.

Das einzige Problem ist, dass die Verbindung für etwa 60 Sekunden geöffnet bleibt. Die Daten werden sofort von PHP an Knoten gesendet, der Webchat antwortet sofort wie ich es möchte. Die Browser-Registerkarte, die das PHP-Skript anfordert, wird für diese 60 Sekunden gedreht und nimmt keine weitere Anfrage an, bis die erste beendet ist.

Ich hatte gedacht, die Aufnahme von fclose() würde es so machen, dass die Verbindung nicht so offen bleibt.

Ich habe auch versucht die Header zu ändern zu: Anschluss: Schließen UND/ODER versucht GET/HTTP/1.0 (Stand ein paar Ratschläge von anderen Threads mit ähnlichen Problemen) Dies entweder nicht helfen.

Irgendwelche Ratschläge, wie ich das PHP-Skript dazu bringen kann, die Verbindung sofort zu schließen, oder der Node-Server die Verbindung vom LAMP-Server löscht, sobald es seine Anfrage bearbeitet hat?

+1

Verwendet Ihr node.js-Dienst Redis oder einen anderen Pub/Sub-Dienst für die horizontale Skalierung? Wenn dies der Fall ist, können Sie wahrscheinlich den Dienst veröffentlichen, anstatt eine Verbindung zu node.js über Websockets herzustellen. – Myst

Antwort

3

Kommen Sie tatsächlich zu fclose()?

Der Code könnte auf fread() geklebt werden, wenn Sie es auf blockierenden Modus haben (http://php.net/manual/en/function.stream-set-blocking.php) und senden Sie nicht 2000 Byte oder eof - in anderen Worten fread wird nur eine Auszeit.

Zum Test: - setzen echo 'Closing'; vor fclose so dass Sie wissen, Sie bekommen es - fügen stream_set_blocking ($sock, false); - verkürzen die Socket-Timeout (http://php.net/manual/en/function.stream-set-timeout.php)

Kleine Warnung: Wenn Sie nicht blockierend gesetzt, können Sie Schleife um $headers = fread($sock, 2000); als das wird sofort zurückkehren, auch wenn keine Daten vorhanden sind. Erstellen Sie eine while-Schleife, bei der die Schleife nicht ausgeführt wird, wenn Sie die Zeit manuell verlängern, oder Sie erhalten die Antwort, die Sie erwarten.

+0

Sie hatten Recht, es war nicht einmal zu schließen(). Und dein Hinweis auf das 2000 in fread() war genau richtig. Ich habe nicht bemerkt, dass definierte Bytes zurückgegeben werden. In all den Fällen, die ich hier verwenden möchte, interessiert mich eigentlich keine Antwort von der WebSocket. Also experimentierte ich mit der Änderung der zweiten Eingabe auf Null anstelle von 2000. PHP mochte das nicht. Also habe ich versucht den Wert auf 1 zu ändern. Und jetzt funktioniert alles perfekt. Aber ich mache mir immer noch Sorgen, dass ich nicht genau verstehe, was passiert. Glauben Sie, dass es eine vertretbare Lösung ist, diesen Wert auf 1 zu setzen? – rgbflawed

+0

@rgbflawed Wenn Sie keine Antwort vom Server erwarten, rufen Sie 'fread' nicht auf.Entweder sende 'hybi10Encode (1000, 'close')' (oder ähnlich), um die Close-Sequenz sauber vom Server zu initiieren, oder rufe 'fclose' sofort an. – bishop

+0

Wie Bischof sagte! Schließen Sie einfach, ohne zu versuchen, die Antwort zu lesen, wenn nie eine Antwort zu lesen ist. Wenn Sie jedoch den nicht blockierenden Modus mit einem manuellen Zeitlimit verwenden, erhalten Sie mehr Kontrolle über das erste fread, aber halten Sie es einfach, indem Sie im Blockiermodus bleiben und nicht die zweite Antwort lesen. – Robbie

2

WebSockets ist eine bidirektionale persistente Verbindung. Es wird erwartet, dass die Verbindung offen bleibt, bis entweder der Client sie explizit schließt oder der Server dies tut. Da Sie kein geschlossenes Paket senden (der zweite Parameter zu hybi10Encode wäre 'close'), wartet der Server auf weitere Kommunikation und legt dann nach Stille auf.

Also, wie machst du das? Nun, unter Angabe RFC 6455 § 7.1.1:

Um Schließen Sie das WebSocket-Verbindung, ein Endpunkt der schließt zugrunde liegende TCP-Verbindung.Ein Endpunkt SOLLTE eine Methode verwenden, die die TCP-Verbindung sowie die TLS-Sitzung sauber schließt, wenn anwendbar ist und alle nachfolgenden Bytes verworfen werden, die möglicherweise empfangen wurden. Ein Endpunkt KANN die Verbindung bei Bedarf mit allen Mitteln schließen, z. B. bei einem Angriff.

Die zugrunde liegende TCP-Verbindung, in den meisten normalen Fällen sollten zuerst vom Server geschlossen werden, so dass sie den Zustand TIME_WAIT halten und nicht den Client (wie dies aus erneutem Öffnen die Verbindung für 2 verhindern würde maximale Segmentlebensdauer (2MSL), während es keine entsprechende Serverauswirkung gibt, da eine TIME_WAIT-Verbindung sofort wieder geöffnet wird ( ein neues SYN mit einer höheren Seq-Nummer). In abnormalen Fällen (wie z. B. nicht eine TCP-Close vom Server nach einer angemessenen Menge der Zeit erhalten) kann ein Client die TCP-schließen. Als solcher, wenn ein Server zu die WebSocket-Verbindung Schließen Sie angewiesen wird es ein TCP Schließen sofort einleiten und wenn ein Client angewiesen, die dies ebenfalls zu tun, sollte es Schließen von dem Server für eine TCP warten.

Die sauber schließt es bedeutet, wie 1000 die preferrable Art und Weise, ein close frame mit einem entsprechenden Statuscode es sendet zu tun.

Ich vermute, das passiert nicht wegen Ihrer zweiten fread(..., 2000). Der Server hat dir 2000 Bytes nicht gesendet, also wartet fread darauf. Möglicherweise müssen Sie dort einen Chunked-Lesevorgang durchführen oder etwas, das für die zurückkommenden Daten spezifischer ist. Ich würde nicht in einer engen Schleife drehen.

Überprüfen Sie auch den Rückkehrcode fclose. Wenn es scheitert, deutet das auf ein zu untersuchendes Problem hin (obwohl ich bezweifle, dass dies hier der Fall wäre).

ich auch würde vorschlagen, ein hybi10Encode(1000, 'close') oder so nach Senden Sie haben erfolgreich fread alle Daten, die Sie in der Antwort wollen die Verbindung per RFC sauber zu schließen.

+0

Sowohl Sie als auch Robbie gaben sehr ähnliche und sehr geschätzte Antworten. Ich habe einen zusätzlichen Kommentar zu seiner Frage gemacht, aber ich hätte es auch hier gut ausdrücken können. – rgbflawed

+0

Ich bin genervt, dass es mich nicht auch ein gleiches separates Kopfgeld für dich starten lässt. Ich habe stattdessen eine Menge deiner anderen Antworten aufgewertet. Danke noch einmal! – rgbflawed

+0

@rgbflawed Hilfe ist eine Belohnung. Froh, dass du das funktioniert hast! – bishop

0

Ihr Problem ist eigentlich ganz einfach,

$headers = fread($sock, 2000); 
//see the second link above for what hybi10Encode() is doing 
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr); 
$wsdata = fread($sock, 2000); 
fclose($sock); 

Ihre Lese 4000 Bytes, aber wenn die Nachrichten sind nicht 4000 Byte lang php warten, bis Socket Timeout auftritt, gibt es 2 Möglichkeiten 1 Option ist Ihre Ihre bekommen Node-Server, um die Verbindung am Ende zu beenden und dann php es wird aufhören zu lesen. der zweite wäre es, alles zu lesen. mit feof(), aber die Art und Weise Ihrer Verwendung würde dies eine Überarbeitung von Server und Client erfordern.

Verwandte Themen