Seit einiger Zeit spiele ich immer mal wieder mit Websockets. Allerdings habe ich bisher keinen wirklich funktionstüchtigen, in PHP implementierten Websocket-Client finden können. Also habe ich kurzerhand selbst eine Klasse dafür gebaut welche ich hier natürlich gerne veröffentliche. Absolut minimal, aber für Testzwecke ausreichend. Es wird aktuell der Handshake aus Draft “draft-ietf-hybi-thewebsocketprotocol-00” unterstürzt. Getestet habe ich es mit der aktuellen Version von Chrome (13.0.782) und dem Websocket Server von Nico Kaiser.
Hier die Klasse + Beispielaufruf:
ACHTUNG: Eine aktuellere Version dieser Klasse gibt es hier: PHP Websocket Server/Client nach Draft hybi-10
/** * Very basic websocket client. * Supporting handshake from drafts: * draft-hixie-thewebsocketprotocol-76 * draft-ietf-hybi-thewebsocketprotocol-00 * * @author Simon Samtleben <web@lemmingzshadow.net> * @version 2011-09-15 */ class WebsocketClient { private $_Socket = null; public function __construct($host, $port) { $this->_connect($host, $port); } public function __destruct() { $this->_disconnect(); } public function sendData($data) { // send actual data: fwrite($this->_Socket, "\x00" . $data . "\xff" ) or die('Error:' . $errno . ':' . $errstr); $wsData = fread($this->_Socket, 2000); $retData = trim($wsData,"\x00\xff"); return $retData; } private function _connect($host, $port) { $key1 = $this->_generateRandomString(32); $key2 = $this->_generateRandomString(32); $key3 = $this->_generateRandomString(8, false, true); $header = "GET /echo HTTP/1.1\r\n"; $header.= "Upgrade: WebSocket\r\n"; $header.= "Connection: Upgrade\r\n"; $header.= "Host: ".$host.":".$port."\r\n"; $header.= "Origin: http://foobar.com\r\n"; $header.= "Sec-WebSocket-Key1: " . $key1 . "\r\n"; $header.= "Sec-WebSocket-Key2: " . $key2 . "\r\n"; $header.= "\r\n"; $header.= $key3; $this->_Socket = fsockopen($host, $port, $errno, $errstr, 2); fwrite($this->_Socket, $header) or die('Error: ' . $errno . ':' . $errstr); $response = fread($this->_Socket, 2000); /** * @todo: check response here. Currently not implemented cause "2 key handshake" is already deprecated. * See: http://en.wikipedia.org/wiki/WebSocket#WebSocket_Protocol_Handshake */ return true; } private function _disconnect() { fclose($this->_Socket); } private function _generateRandomString($length = 10, $addSpaces = true, $addNumbers = true) { $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}'; $useChars = array(); // select some random chars: for($i = 0; $i < $length; $i++) { $useChars[] = $characters[mt_rand(0, strlen($characters)-1)]; } // add spaces and numbers: if($addSpaces === true) { array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' '); } if($addNumbers === true) { array_push($useChars, rand(0,9), rand(0,9), rand(0,9)); } shuffle($useChars); $randomString = trim(implode('', $useChars)); $randomString = substr($randomString, 0, $length); return $randomString; } } $WebSocketClient = new WebsocketClient('127.0.0.1', 8000); echo $WebSocketClient->sendData('1337'); unset($WebSocketClient); |
11 Responses to PHP Websocket Client
Oliver September 15, 2011
Warum beendest Du die Verbindung? Sinn ist doch eigentlich, dass der Daemon auf dem Port dauerhaft lauscht, oder?
Lemmingz Shadow September 15, 2011
Klar, je nach Anwendungszweck ist natürlich auch ein dauerhafte Verbindung sinnvoll. Ich nutze den Client allerdings quasi als API um darüber Nachrichten an alle anderen Clients (etwa per JS im Browser) zu schicken.
Oliver September 15, 2011
Aber dafür muss die Verbindung doch auch persistent sein!?
Lemmingz Shadow September 15, 2011
Nope. In dem Moment wo ein Request an die API geht baue ich eine Verbindung zum Websocket-Server auf, die entsprechende Nachricht wird gesendet und die Verbindung wird wieder getrennt. Wie oben schon gesagt, sind das momentan aber alles nur Tests…
Oliver September 15, 2011
Ja, aber der Sinn ist doch, dass der Server auch an den Client funken kann, oder? Ansonsten kannst Du auch die Sachen einfach per http abrufen. *verwirrt-bin*
Oliver September 15, 2011
Achso, kennst Du das? http://code.google.com/p/phpwebsocket/
Oliver September 15, 2011
Ach sorry – Client, nicht Server m(
Lemmingz Shadow September 16, 2011
Hi, ja kenne ich.
Zum Sinn des Ganzen: Angenommen du hast ein Webseite auf der Daten live angezeigt werden sollen. Dann hast du verschiedene Besucher die persistent mit dem Websocket-Server verbunden sind und sozusagen auf Updates warten ohne ständig die Seite neu zu laden oder per Ajax zu pollen o.Ä.
Auf dem Server läuft nun beispielsweise ein Cronscript und hat neue Informationen für die Webseite, also verbindet es sich über den PHP-Client mit dem Websocket-Server, sendet die Infos an alle Benutzer und trennt die Verbindung wieder. Dafür ist eine persistente Verbindung nicht notwendig.
lifeofguenter September 16, 2011
hmm eigentlich sehr interessanter ansatz.. also anstatt dass der WebsocketServer dafür zuständig ist aus X-Quelle daten zu ziehen und an den Clients zu schicken, wird es quasi über einen “master”-client erledigt? Oder habe ich was falsch verstanden?
Wusste nämlich nicht dass Clients über WebSockets auch Daten schicken können – dachte das wäre reines polling/push
Lemmingz Shadow September 16, 2011
Also die Websocket-Verbindung ist bidirektional, sprich alle Clients können theoretisch senden und empfangen. (Webchat z.B.) Der PHP-Client oben ist allerdings nur zum senden gedacht. Natürlich kann die Antwort auf die gesendete Nachricht ausgewertet werden, er lauscht aber nicht am Server auf bestimmte Nachrichten. Das wäre meiner Ansicht nach auch sinnlos, da ich das ja direkt am Server viel einfacher erledigen kann.
Und ja, ich gebe dir recht, es ist sinnvoller alle Aufgaben die nichts mit dem eigentlich Websocket-Server zu tun haben an anderer Stelle zu erledigen und dann nur das jeweilige Ergebnis an den Server zu senden. Genau dafür brauchte ich ein Client-Klasse
WitNt2000 December 20, 2012
Good day, colleague!
I would like to ask You some questions. A found this actile http://lemmingzshadow.net/379/php-websocket-client/ and tested it.
The test showed this warnings:
Warning: fsockopen() [function.fsockopen]: unable to connect to ws://echo.websocket.org/:80 (Unable to find the socket transport “ws” – did you forget to enable it when you configured PHP?) in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 44
Warning: fwrite(): supplied argument is not a valid stream resource in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 45
Error: 151557008:Unable to find the socket transport “ws” – did you forget to enable it when you configured PHP?
Warning: fclose(): supplied argument is not a valid stream resource in /home/onegame192/domains/roadwebcamera.com/public_html/ws_test.php on line 58
What’s wrong?
Thank You for future answer.